How to populate collection controls in a WPF custom control? - wpf

I'm learning the ins and outs of creating custom controls in WPF. The concepts I understand thus far:
The code defines the control's behavior.
The template defines the control's look and feel, the visual aspect.
You can bind elements of the template to properties of the underlying control object.
If the template has multiple collection controls, such as StackPanels, how do you bind them so that they are populated by underlying collections? My first inclination was to make use of the DataContext of each StackPanel, but I couldn't make it work. I get the feeling I'm missing a key concept that would solve this for me.

What did you want to be in these StackPanels? Panels are used for arranging items. If you want to show a collection of items, you probably want to be using an ItemsControl (or one of the many types of ItemsControls). An ItemsControl is very powerful- you can specify how the items are displayed and how the panel that they're displayed on is displayed as well. You can even specify that the panel is a StackPanel. For example,
<ItemsControl ItemsSource="{Binding ElementName=root, Path=List1}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!-- Template defines the panel -->
<StackPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Template defines each item -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then you wanted to bind to a list of items, which is very easy with an ItemsControl! In your case with the custom control, you may want to expose dependency properties on the control itself in the code-behind and then bind to those from the XAML. For example, you can create dependency properties for the various lists you have like this:
public static readonly DependencyProperty List1Property = DependencyProperty.Register(
"List1",
typeof(IList<string>),
typeof(MyControl));
public static readonly DependencyProperty List2Property = DependencyProperty.Register(
"List2",
typeof(IList<string>),
typeof(MyControl));
Then you can bind the ItemsSource property of your ItemsControls:
<ItemsControl ItemsPanel="..." ItemsSource="{Binding ElementName=root, Path=List1}" />
<ItemsControl ItemsPanel="..." ItemsSource="{Binding ElementName=root, Path=List2}" />
(in this case I assume that the custom control has a x:Name="root")
I hope this helps!

You can't bind the contents of a StackPanel directly. It's simply a layout control.
What you could do, however, is use a ListBox and set its ItemsPanel to be a StackPanel (or whatever other layout control you need). Then you can set the ListBox's ItemsSource to whatever underlying collection you want.

Related

MVVM View Binding

Lets say I have a list of items. So I have 2 views: ListView and ItemView.
I also have 2 ViewModels: ListViewModel and ItemViewModel.
In ListViewModel I create and initialize ObservableCollection of ItemViewModels.
In ListView I have ListBox with ItemTemplate -> ItemView, and ItemSource binded to the ObservableCoolection into the ListViewModel.
I want to bind each ItemView to each ItemViewModel so I will be able to use the binding into ItemView.xaml, but I cannot achive it.
What I understood from your question is you want to bind ItemViewModel to ItemView.xaml and your ListBox resides in some other xaml(say ListBox.xaml). You don't want to apply binding related to ItemViewmodel and ItemVIew in ListBox.xaml. If this was the issue, you
can easily create a DataTemplate mapping to achieve so:
<Window
xmlns:vw="namespace of your view files (i.e. xaml files. ListBox.xaml and ItemView.xaml)"
xmlns:ViewModels="namespace of your view model files (i.e. ItemViewModel.cs, ListBoxViewModel.cs)">
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModels:ItemViewModel}">
<vw:ItemView />
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=List of ItemViewmodel in ListBoxViewModel.cs}"
</Grid>
</Window>
Then, you ItemViewModel class will be mapped with ItemView as it is specified in Resources without any key. Now you can apply binding in ItemView.xaml. Look, you do not need to assign Itemtemplate of ListBox here, that will be resolved automatically. If you want to specify the ItemTemplate anyway (and do not want to specify in resource), then write this:
<ListBox.ItemTemplate>
<DataTemplate>
<vw:ItemView />
</DataTemplate>
</ListBox.ItemTemplate>
Either way should work :)

View Model, Dependency Properties Confusion

I am developing a custom WPF control and confused how to use the dependency property. My view model contains 2 properties:
class Customer {
string Name;
string ID;
}
My custom control is responsible for displaying these fields.
Q1: Do I need to define any dependency properties (eg "Name", "ID") in my custom control?
Q2: I am using ItemsControl to display a list of Customers. How is the Customer object passed to my custom control? Is it done through the DataContext or do I need to add a "Customer" dependency property in my control and in the xaml, bind "Customer" to "something" (what's that something)?
<ItemPresenter>
<MyCustomControl Customer="??what should i put here???"/>
</ItemPresender>
Q1. Why do you have a custom control? Ordinarily, you would just create a UserControl (there is a distinction in WPF between UserControl and custom control) and bind properties within your UserControl to properties of your view model. For example (let's call this CustomerView):
<UserControl ...>
<StackPanel>
<TextBlock Text="{Binding ID}"/>
<TextBox Text="{Binding Name}"/>
</StackPanel>
</UserControl>
Q2. Through the DataContext. For example, you might have something like this:
<ItemsControl ItemsSource="{Binding Customers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CustomerView/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Each item generated by the ItemsControl will have the related data item set as its DataContext. Hence, each CustomerView will have the appropriate Customer as its DataContext.

Switch View based on selected TreeViewItem

I have a Shell.xaml file which contains two other UserControls. On the left is my TreeView and on the right is a detail screen.
I want the detailscreen to be switchable based on a selected TreeViewItem. I know this can be achieved by using DataTemplates, because I've done it with simple button clicks and using the <ContentControl Content="{Binding CurrentDetailViewModel}"> tag to accomplish this, but I have no idea how to accomplish this based on a selected TreeViewItem. I also have a separate ViewModel class for my UserControl which holds my TreeView and a separate for each detail screen.
I've been using Josh Smith's tutorial on TreeViews: http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx
So I also do use the TreeViewItemViewModel.cs class of his.
Could someone shed some light onto this?
Thanks,
Grant
If both the treeview and the details are displaying the same object (i.e the ItemsSource of the treeview contains the objects that you want to data template in the custom control) then you should be able to set a property on an underlying ViewModel that both controls share and have the custom control display something relevant with data templates.
for example, in the ViewModel:
object TreeViewSelectedItem
{
get{ return _treeViewSelectedItem;}
set {_treeViewSelectedItem = value; NotifyPropertyChanged("TreeViewSelectedItem");}
}
Treeview xaml
<TreeView ... SelectedItem={Binding TreeViewSelectedItem Mode=OneWayToSource}".../>
custom control xaml
<UserControl>
<Control.Resources>
<DataTemplate DataType="{x:Type Plane}">
....
</DataTemplate>
<DataTemplate DataType="{x:Type Train}">
....
</DataTemplate>
<DataTemplate DataType="{x:Type Automobile}">
....
</DataTemplate>
</Control.Resources>
<ContentControl Content={Binding TreeViewSelectedItem}"/>
</Usercontrol>

Dynamically adding controls to View from Controller

In WPF, I am using the MVVM model.
I have a View with a UniformGrid and a ViewModel where I would like to add items to the UniformGrid how would I accomplish this without doing it in the code behind?
The UniformGrid is a Panel; it's intent is not what you are trying to accomplish. You can achieve what you are trying to do however by adjusting the default ItemsPanelTemplate within an ItemsControl.
<ItemsControl ItemsSource="{Binding PropertyNameOnViewModel}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
This will allow you to bind to your ViewModel property within the ItemsControl and the data will get represented visually within a UniformGrid.

WPF: How do I use two different controls in an ItemsControl?

I have a WPF ItemsControl who's ItemsSource is bound to an observable collection of view models in MVVM. The ItemTemplate is set to the user control that I want. However, there are instances when I would like another control instead of the one specified in XAML.
How can I easily do this?
Use DataTemplates to map view models to views:
<ItemsControl ItemsSource="{Binding SomeCollectionOfViewModels}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:FirstViewModel}">
<Label>Foo</Label>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SecondViewModel}">
<Label>Bar</Label>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
If I understand you have a collection that contains two different type of object and you want 2 different template.
You could build a datatemplate for each object type and leave WPF to render the right template based on the object type.

Resources