Dynamic pages using Pivot control - c#

Being new to the Windows 7/8 phone development arena... I am enjoying working with the windows phone tremendously... however there has been a learning curve and so much to get to know.
With that said, what I am trying to do is create a page that that is dynamically bound to a data structure that will display n number of pivot pages, and each pivot page will have different XAML to display the content.
I looked over this code project article (http://www.codeproject.com/Articles/113152/Applying-Data-Templates-Dynamically-by-Type-in-WP7) and it uses a list box to control the display... but what I am interested in is doing the same thing, but with a pivot page.
I learn best by example... here are the classes for binding the data to the controls, that I would LIKE TO USE ...
public class ParkingLot : List<Car>
{
public ParkingLot() { }
// this will be the pivot page title
public string Lot { get; set; }
// the list of cars will be displayed on the page
}
public class Car
{
public Car() { }
// this will be the data that is displayed in the pivot page for each car
public string Width { get; set; }
public string Length { get; set; }
}
public class Library : List<Book>
{
public Library() { }
// this will be the pivot page title
public string Location { get; set; }
// the list of books will be displayed on the page
}
public class Book
{
public Book() { }
// this is the data that will be displayed for each book
public string ISBN { get; set; }
public string Title { get; set; }
}
I don't know if it would be better to post all the code here... or just to have you all look at the article on Code project, I will post up the code that I modified from the article... in hopes that somebody can help me figure this out:
xaml :
<phone:PhoneApplicationPage x:Class="dynDataTemplateTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:loc="clr-namespace:dynDataTemplateTest.View"
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait"
Orientation="Portrait"
mc:Ignorable="d"
d:DesignWidth="480"
d:DesignHeight="768"
shell:SystemTray.IsVisible="True"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<!--LayoutRoot contains the root grid where all other page content is placed-->
<Grid x:Name="LayoutRoot"
Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel"
Grid.Row="0"
Margin="24,24,0,12">
<TextBlock x:Name="ApplicationTitle"
Text="{Binding ApplicationTitle}"
Style="{StaticResource PhoneTextNormalStyle}" />
<TextBlock x:Name="PageTitle"
Text="{Binding PageName}"
Margin="-3,-8,0,0"
Style="{StaticResource PhoneTextTitle1Style}" />
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentGrid"
Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<loc:DynamicContentControl Content="{Binding SelectedItem}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
<controls:Pivot ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Path=SelectedItem}" >
<controls:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}" FontSize="30" FontWeight="Bold" Margin="5"/>
</DataTemplate>
</controls:Pivot.HeaderTemplate>
<controls:Pivot.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"></StackPanel>
</ItemsPanelTemplate>
</controls:Pivot.ItemsPanel>
</controls:Pivot>
</Grid>
</Grid>
Here is the DataTemplateSelector class
public static class DataTemplateSelector
{
public static DataTemplate GetTemplate(ViewModelBase param)
{
Type t = param.GetType();
return App.Current.Resources[t.Name] as DataTemplate;
}
}
Here is the dynamic Content control:
public class DynamicContentControl:ContentControl
{
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
this.ContentTemplate = mSator.Model.DataTemplateSelector.GetTemplate(newContent as ViewModelBase);
}
}
Here is the first view xaml:
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<TextBlock Margin="20" Foreground="Green" FontSize="32"
FontWeight="Bold" Text="{Binding Path=FirstProperty}"
></TextBlock>
</Grid>
( the second view xaml can be the first view, just change the color )
Here is the FirstViewModel class (from the article)
public class FirstViewModel : SelectableViewModel
{
public FirstViewModel()
{
DisplayName = "First";
FirstProperty = "this is the first property";
}
private string firstProp;
public string FirstProperty
{
get { return firstProp; }
set
{
if (firstProp != value)
{
firstProp = value;
RaisePropertyChanged("FirstProperty");
}
}
}
}
Here is the SelectableView Model class
public class SelectableViewModel : ViewModelBase
{
public SelectableViewModel()
{
}
string dispName;
public string DisplayName
{
get { return dispName; }
set
{
if (dispName != value)
{
dispName = value;
RaisePropertyChanged("DisplayName");
}
}
}
}
Here is the main view model class:
public class MainViewModel : ViewModelBase
{
public string ApplicationTitle
{
get
{
return "Dynamic Data Templates";
}
}
public string PageName
{
get
{
return "Main page";
}
}
private List<SelectableViewModel> viewModels;
public MainViewModel()
{
viewModels = new List<SelectableViewModel>();
viewModels.Add(new FirstViewModel());
viewModels.Add(new SecondViewModel());
SelectedItem = viewModels[0];
}
public List<SelectableViewModel> Items
{
get
{
return viewModels;
}
}
SelectableViewModel selItem;
public SelectableViewModel SelectedItem
{
get { return selItem; }
set
{
if (selItem != value)
{
selItem = value;
RaisePropertyChanged("SelectedItem");
}
}
}
}
Thanks again for helping !

As you say you're still learning, let me explain why having n number of Pivot items is a bad idea:
You will likely run into performance issues due to the amount of content on a single page. With a list the items can be virtualized. The Pivot control does not support virtualization of dynamically added PivotItems.
It's hard for people to navigate to a desired item when there are lots of PivotItems as there isn't a way to quickly get to the one that is wanted. Say you had 30 items in the pivot but wanted to get to the 15th one. That would require a lot of swiping and if doing it quickly it would be easy to go past the one that was wanted.
The Pivot Control is intended to be used for one of two purposes:
To show different views of a set of data. i.e. The email app shows different views of a mailbox in each PivotItem, filtered for "all", "unread", "flagged" and "urgent".
To show different pieces of related data. i.e. When viewing an individual contact/person you see different related actions and information grouped into the different PivotItems: "profiles", "what's new", "photos" and "history".
It is not the intention that the Pivot control should be used as a container for vast quantities of content, such as n collections of template lists.
It is suggested that the maximum number of items in a pivot should be 7 to avoid issues with performance and usability.
All in all, not using the Pivot control in one of the ways it was intended can cause performance issues for you as a developer and usability issues for the people using the app.
Both of which are scenarios to be avoided.
Sorry this isn't a direct answer to your question but hopefully it will help you develop a better app (or apps). ;)

Related

WPF Navigation while Keeping Menubar (Header) and Footer Fixed

We used to develop application with WinForms and nowadays we are trying to migrate it to WPF, starting from zero. In our application we have 3 main parts on screen which are Header (all main menu items), body (based on MDI container, content can be changed) and the footer (where general status is displayed, logo etc.) Whenever a user clicks on different menuitem from header part, the body part would change it's children to that Panel/Form.
There are lot's of good examples/tutorials on the Internet but I am confused about how to achieve to create a navigation service that allows to switch the view of body part.
Any suggestions would be appriciated, thanks in advance.
There are indeed multiple ways to archive this result. I will try and explain the very basic/easiest way to get the result.
While this will not provide example in combination with Menu Control, I think it will help you to understand the concept
In your MainWindow you can split use Grid layout and split the space into 3 parts as you wanted. Your Main window Xaml should look something like this :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<ContentControl x:Name="Header"/>
<ContentControl x:Name="Content" Grid.Row="1/>
<ContentControl x:Name="Footer" Grid.Row="2"/>
</Grid>
In your content control you can insert your "UserControls" for the Header,Content,Footer. Now to the navigation part:
As mentioned there are many ways to archive this and I will describe what I do consider the easiest way (not the most flexible way however, so keep that in mind).
First I will suggest to make a navigation Model:
public class NavigationModel
{
public NavigationModel(string title, string description, Brush color)
{
Title = title;
Description = description;
Color = color;
}
public string Title { get; set; }
public string Description { get; set; }
public Brush Color { get; set; }
public override bool Equals(object obj)
{
return obj is NavigationModel model &&
Title == model.Title &&
Description == model.Description &&
Color == model.Color;
}
public override int GetHashCode()
{
return HashCode.Combine(Title, Description, Color);
}
}
We create a new class that will handle the navigation collection, lets call it navigation service.
public class NavigationService
{
public List<NavigationModel> NavigationOptions { get=>NavigationNameToUserControl.Keys.ToList(); }
public UserControl NavigateToModel(NavigationModel _navigationModel)
{
if (_navigationModel is null)
//Or throw exception
return null;
if (NavigationNameToUserControl.ContainsKey(_navigationModel))
{
return NavigationNameToUserControl[_navigationModel].Invoke();
}
//Ideally you should throw here Custom Exception
return null;
}
//Usage of the Func, provides each call new initialization of the view
//If you need initialized views, just remove the Func
//-------------------------------------------------------------------
//Readonly is used only for performance reasons
//Of course there is option to add the elements to the collection, if dynamic navigation mutation is needed
private readonly Dictionary<NavigationModel, Func<UserControl>> NavigationNameToUserControl = new Dictionary<NavigationModel, Func<UserControl>>
{
{ new NavigationModel("Navigate To A","This will navigate to the A View",Brushes.Aqua), ()=>{ return new View.ViewA(); } },
{ new NavigationModel("Navigate To B","This will navigate to the B View",Brushes.GreenYellow), ()=>{ return new View.ViewB(); } }
};
#region SingletonThreadSafe
private static readonly object Instancelock = new object();
private static NavigationService instance = null;
public static NavigationService GetInstance
{
get
{
if (instance == null)
{
lock (Instancelock)
{
if (instance == null)
{
instance = new NavigationService();
}
}
}
return instance;
}
}
#endregion
}
This service will provide us with action to receive desired UserControll (note that I am using UserControl instead of pages, since they provide more flexibility).
Not we create additional Converter, which we will bind into the xaml:
public class NavigationConverter : MarkupExtension, IValueConverter
{
private static NavigationConverter _converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter is null)
{
_converter = new NavigationConverter();
}
return _converter;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
NavigationModel navigateTo = (NavigationModel)value;
NavigationService navigation = NavigationService.GetInstance;
if (navigateTo is null)
return null;
return navigation.NavigateToModel(navigateTo);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> null;
}
In our MainWindows.xaml add reference to the Converter namespace over xmlns, for example :
xmlns:Converter="clr-namespace:SimpleNavigation.Converter"
and create insance of converter :
<Window.Resources>
<Converter:NavigationConverter x:Key="NavigationConverter"/>
</Window.Resources>
Note that your project name will have different namespace
And set the Add datacontext to the instance of our Navigation Service:
You can do it over MainWindow.Xaml.CS or create a ViewModel if you are using MVVM
MainWindow.Xaml.CS:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Service.NavigationService.GetInstance.NavigationOptions;
}
}
Now all is left to do is navigate. I do not know how about your UX, so I will just provide example from my github of the MainWindow.xaml. Hope you will manage to make the best of it :
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel>
<ListView
x:Name="NavigationList"
ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<Border
Height="35"
BorderBrush="Gray"
Background="{Binding Color}"
ToolTip="{Binding Description}"
BorderThickness="2">
<TextBlock
VerticalAlignment="Center"
FontWeight="DemiBold"
Margin="10"
Text="{Binding Title}" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<ContentControl
Grid.Column="1"
Content="{Binding ElementName=NavigationList,Path=SelectedItem,Converter={StaticResource NavigationConverter}}"/>
</Grid>
Just in case I will leave you a link to github, so it will be easier for you
https://github.com/6demon89/Tutorials/blob/master/SimpleNavigation/MainWindow.xaml
Using same principle to use Menu Navigation
<Window.DataContext>
<VM:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<Converter:NavigationConverter x:Key="NavigationConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="Navigaiton"
ItemsSource="{Binding NavigationOptions}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem
Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding}"
Header="{Binding Title}"
Background="{Binding Color}"
ToolTip="{Binding Description}">
</MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
<ContentControl
Grid.Row="1"
Background="Red"
BorderBrush="Gray"
BorderThickness="2"
Content="{Binding CurrentView,Converter={StaticResource NavigationConverter}}"/>
<Border Grid.Row="2" Background="{Binding CurrentView.Color}">
<TextBlock Text="{Binding CurrentView.Description}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
And we have in VM List of the navigation Models, Current Model and the navigation command :
public class MainViewModel:INotifyPropertyChanged
{
public List<NavigationModel> NavigationOptions { get => NavigationService.GetInstance.NavigationOptions; }
private NavigationModel currentView;
public NavigationModel CurrentView
{
get { return currentView; }
set
{
currentView = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentView"));
}
}
RelayCommand _saveCommand;
public event PropertyChangedEventHandler PropertyChanged;
public ICommand NavigateCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(Navigate);
}
return _saveCommand;
}
}
private void Navigate(object param)
{
if(param is NavigationModel nav)
{
CurrentView = nav;
}
}
}
Sorry for long reply
I think that you not necessary have to start from scratch. You may have a look:
https://qube7.com/guides/navigation.html

Showing a single value from api call TextBlock (MVVM)

I have been stuck on this for a few hours now. I am using the IGDB api for the application I'm building. So, I'm trying to get the ListBox I have to show the Game Title, cover and Developer. I've managed to get all of them to show up in the list with no issues but the main issue comes in when I just want to show a single developer within the list, the list is showing all companies involved plus the developer because the API returns an array of Involved Companies and within those Involved Companies the developer body returns a Boolean value to say which one is the developer. I can see it in my head, if developer is true then show the developer and remove the other involved companies. Here is an image of what is showing up when I run my query:
Query Image
I have outlined it in red. From that list all I would need is Bungie since they're the developers. So in my view I have a ListBox and a ListView nested within it:
BrowseGamesView
<ListBox ItemsSource="{Binding Games}"
Width="390"
Height="500"
Grid.Row="4">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Margin="0 0 10 0">
<Image Source="{Binding cover.image_id, Converter={StaticResource stringToImage}}"
Stretch="Fill"
Height="70"
Width="60"/>
</Border>
<TextBlock Text="{Binding name}"
FontWeight="Bold"
Grid.Column="1"
Grid.Row="0"/>
<ListView Grid.Column="1"
Grid.Row="1"
ItemsSource="{Binding involved_companies}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding company.name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Within my ViewModel I have a method for making the query and also holds the ObservableCollection (Games).
BrowseGamesViewModel
public ObservableCollection<Game> Games { get; set; }
public async void MakeQuery()
{
var games = await IGDBHelper.GetGames(Query);
Games.Clear();
foreach(var game in games)
{
Games.Add(game);
}
}
In my Helper class I get the games
IGDBHelper
public static async Task<List<Game>> GetGames(string query)
{
List<Game> games = new List<Game>();
string myJson = "search \"{0}\"; fields name, cover.url, cover.image_id, involved_companies.developer, involved_companies.company.name; limit 500;";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("user-key", "key hidden");
var response = await client.PostAsync(BASE_URL_GAMES, new StringContent(string.Format(myJson, query), Encoding.UTF8, "application/json"));
var responseString = await response.Content.ReadAsStringAsync();
games = JsonConvert.DeserializeObject<List<Game>>(responseString);
}
return games;
}
And all of models are concrete classes for the Json
public class Game
{
public int id { get; set; }
public Cover cover { get; set; }
public List<InvolvedCompany> involved_companies { get; set; }
public string name { get; set; }
}
public class InvolvedCompany
{
public int id { get; set; }
public Company company { get; set; }
public bool developer { get; set; }
}
public class Company
{
public int id { get; set; }
public string name { get; set; }
}
public class Cover
{
public int id { get; set; }
public string image_id { get; set; }
public string url { get; set; }
}
So, to reiterate, I need to somehow just show the developers name by removing the rest of the involved companies from the list. I have tried several things from converters to trying it in the converter class and each time I get exceptions for null.
I would add a new readonly property to the Game class:
public List<InvolvedCompany> DeveloperCompanies
{
get
{
return involved_companies.Where(c => c.developer == true).ToList();
}
}
And then you can bind this new property to your ListView:
<ListView Grid.Column="1"
Grid.Row="1"
ItemsSource="{Binding DeveloperCompanies}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding company.name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This way you would be showing only the developer companies but you would keep the information about all the involved companies as well, if you need to use them for another purpose.
I also suggest you to use PascalCase for the public members of a class. So, for example involved_companies would become InvolvedCompanies.
First, I would suggest to name your properties in PascalCase.
Now for your problem, I suggest to filter the companies by developer property and assign a new list of involving companies to the game before adding it to the list.
foreach(var game in games)
{
game.involved_companies = game.involved_companies.Where(e => e.developer == true).Tolist();
Games.Add(game);
}
Not tested but I believe this should work.

Set itemtemplate for a listbox inside usercontrol

I have a Listbox inside a UserControl to manage different entities with different properties. The UserControl is the same for all entities.
I use MVVM and i bind a generic ViewModel as DataContext for the UserControl.
I wish to set the ItemTemplate for the Listbox in the container xaml for the UserControl in order to display the entity properties.
For the entity "Emlployee" I need to display FullName, for the entity Certifying I need to display CertifyingAuthor and CertifyingDate and so on.
What I need is something similar to that
<StackPanel Grid.Row="0" Orientation="Vertical">
<uc:SearchableFromListTextBox ItemTemplateForTheInsideListBox="{StaticResource Something}" ></uc:SearchableFromListTextBox>
Should I add a dependencyProperty ItemTemplateForTheInsideListBoxProperty to the UserControl? And how i could pass it as itemtemplate of the Listbox?
Hope the question is well explained considering my italian native language.
thanks
EDIT : I give up. This is a control for keyboard data entry and something similar to autocomplete.
Since i am forced to agree to a compromise with MVVM :( i will choose some dirty way to resolve.
Thanks to all
A DataTemplateSelector can do what I think you want. You can define different templates from your user control's XAML, then have the selector choose among them.
Assume these model classes:
public class Employee
{
public Employee(string fullName)
{
FullName = fullName;
}
public string FullName { get; }
}
public class Certifying
{
public Certifying(string certifyingAuthor, DateTime certifyingDate)
{
CertifyingAuthor = certifyingAuthor;
CertifyingDate = certifyingDate;
}
public string CertifyingAuthor { get; }
public DateTime CertifyingDate { get; }
}
Your user control's XAML has a ListBox, which in turn uses a template selector (that we'll get to in a moment) -- and also defines different templates for the two different model types:
<UserControl
x:Class="WPF.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF"
>
<Grid>
<ListBox x:Name="listBox">
<ListBox.ItemTemplateSelector>
<local:MyTemplateSelector>
<local:MyTemplateSelector.EmployeeTemplate>
<DataTemplate DataType="local:Employee">
<TextBlock
Foreground="Red"
Text="{Binding FullName}"
/>
</DataTemplate>
</local:MyTemplateSelector.EmployeeTemplate>
<local:MyTemplateSelector.CertifyingTemplate>
<DataTemplate DataType="local:Certifying">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Foreground="Blue"
Text="{Binding CertifyingAuthor}"
/>
<TextBlock
Grid.Column="1"
Foreground="Green"
Text="{Binding CertifyingDate}"
/>
</Grid>
</DataTemplate>
</local:MyTemplateSelector.CertifyingTemplate>
</local:MyTemplateSelector>
</ListBox.ItemTemplateSelector>
</ListBox>
</Grid>
</UserControl>
The user control's code-behind, where I just assign a list of model objects to the list box (for simplicity):
public partial class UserControl1
{
public UserControl1()
{
InitializeComponent();
listBox.ItemsSource = new List<object>
{
new Employee("Donald Duck"),
new Certifying("Mickey Mouse", DateTime.Now),
new Employee("Napoleon Bonaparte"),
new Certifying("Homer Simpson", DateTime.Now - TimeSpan.FromDays(2)),
};
}
}
And finally, the template selector. Its two properties were set from the user control's XAML; the logic of SelectTemplate decides which one to apply:
public class MyTemplateSelector : DataTemplateSelector
{
/// <summary>
/// This property is set from XAML.
/// </summary>
public DataTemplate EmployeeTemplate { get; set; }
/// <summary>
/// This property is set from XAML.
/// </summary>
public DataTemplate CertifyingTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is Employee)
{
return EmployeeTemplate;
}
if (item is Certifying)
{
return CertifyingTemplate;
}
return base.SelectTemplate(item, container);
}
}
And, for completeness, usage of the user control:
<Grid>
<wpf:UserControl1 />
</Grid>
The end result, where Napoleon is currently selected, and Homer suffers a mouse-over:

Best approach to add an item to a databound Listbox using MVVM pattern?

I'm facing a problem in my WPF project at the moment. At this moment I have a Viewmodel which has a Manager (to communicate with the repo).
internal class TicketViewModel
{
private TicketManager mgr;
public IEnumerable<Ticket> List { get; set; }
public TicketViewModel()
{
mgr = new TicketManager();
List = mgr.GetTickets();
}
}
I've managed to bind this list to the Listbox in my MainWindow. The next step is that I need to add an extra ticket to the list and also pass this through the manager. The problem is I need two parameters from some Controls in the MainWindow. From MVVM perspective I need to use bound Commands on e.g. a Button to communicate with the viewmodel as my viewmodel can't/may not access controls from the window. Is using parameterized Commands the way to go here?
The next problem is that the Listbox won't update I guess. This is the code:
<ListBox x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I found that using a CompareableCollection is the way to go here, but then I still have to read all the Tickets again after adding a new Ticket.
Thanks in advance,
Hicy
okay here is the code.
Lets say you have three textboxes on MainWindow(since you have three Textblocks.) so Your MainWindow.xaml looks like
<Window.DataContext>
<local:MyViewModel/>--set's your viewModel
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="250*"/>
<RowDefinition Height="90"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="TicketNumber" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=Text}" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="Text" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=State}" />
<TextBox x:Name="State" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=TicketNumber}" />
<Button Content="Button" Command="{Binding Path=MainCommand}" Grid.Row="2"/>
</Grid>
and I am assuming that you have some class called class Ticket which contain these three members
Class Ticket
{
public int TicketNumber { get; set; }
public string Text { get; set; }
public string State { get; set; }
}
Now in class TicketManager we fill it with some dummy data
class TicketManager
{
ObservableCollection<Ticket> tl = new ObservableCollection<Ticket>();
internal ObservableCollection<Ticket> GetTickets()
{
tl.Add(new Ticket() { State = "State1", Text = "Text1", TicketNumber = 1 });
tl.Add(new Ticket() { State = "State2", Text = "Text2", TicketNumber = 2 });
tl.Add(new Ticket() { State = "State3", Text = "Text3", TicketNumber = 3 });
return tl;
}
}
and in your Mainwindow ViewModel lets call it MyViewModel.cs we add
class MyViewModel:INotifyPropertyChanged
{
private TicketManager mgr;
public ObservableCollection<Ticket> List { get; set; }
private string text;
private string state;
private int ticketNumber;
private readonly DelegateCommand<object> MyButtonCommand;
public Class1()
{
mgr = new TicketManager();
List = mgr.GetTickets();
MyButtonCommand = new DelegateCommand<object>((s) => { AddListToGrid(text, state, ticketNumber); }, (s) => { return !string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(state); });
}
private void AddListToGrid(string text, string state, int ticketNumber)
{
List.Add(new Ticket() {Text=text,State=state,TicketNumber=ticketNumber });
}
public DelegateCommand<object> MainCommand
{
get
{
return MyButtonCommand;
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged("State");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public int TicketNumber
{
get
{
return ticketNumber;
}
set
{
ticketNumber = value;
OnPropertyChanged("TicketNumber");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can Modify the code in anyway you want
This ViewModel implements fewthings which are very important from MVVM point of view
1) INotifyPropertyChanged
2) WPF Delegate Command
P.S:The code is tested and it runs as expected
Don't get hung up on MVVM it is simply a separation of data from a view, and models are shared between the two with a majority of the business logic (on a shared component) should be performed on the VM; it is not a religion just a three tiered data system. IMHO
If your button needs to do an operation, have it make a call, most likely in the code behind, to a method on the VM which handles the business logic, updates the list with the new item and notifies the manager.
I would bind the list in question to an ObservableCollection which can notify upon insert/delete of an item.

Xamarin Forms listview databinding

I am currently following this guide:
http://www.morganskinner.com/2015/01/xamarin-forms-contacts-search.html
I am willing to make a contacts page in my app, like the one showed in the pictures there, still something is not working out as it should for me:
I wrote/copied the XAML:
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<SearchBar x:Name="search" Placeholder="Search"/>
<ListView x:Name="ProvaView" Grid.Row="1" ItemsSource="{Binding FilteredContacts}" IsGroupingEnabled="true" GroupDisplayBinding="{Binding Key}" GroupShortNameBinding="{Binding Key}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" TextColor="Black" Detail="{Binding PhoneNumber}" DetailColor="Gray">
</TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
and now I am trying to "display something", not dinamically loaded (yet), from a list of data.
I declared a class like this:
public class Contact
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
}
and now the code behind the xaml is this:
public partial class ContactsPage : MasterDetailPage
{
public List<Contact> FilteredContacts = new List<Contact>
{
new Contact
{
Name = "Guido",
PhoneNumber = "3292773477"
},
new Contact
{
Name = "Luca",
PhoneNumber = "3472737445"
},
new Contact
{
Name = "Luca",
PhoneNumber = "3472737445"
}
};
public ContactsPage()
{
InitializeComponent();
ProvaView.ItemsSource = FilteredContacts;
}
}
When I launch the app, all I get anyway is an empty listview, with 3 fields but nothing in it.
Where am I doing wrong?
Also, straight from the page where I am working off, I can't understand the meaning of these Fields associated to the listview:
GroupDisplayBinding="{Binding Key}"
GroupShortNameBinding="{Binding Key}"
and as much as I could look around on the web, I coudln't find any reference to both of them.
Most likely the problem is with the way you declared FilteredContacts, it should be a property, and not a field. The reflection mechanism used in binding only searches for properties, fields are not included.

Categories

Resources