I have a xaml page that has an ItemsControl control on it. ItemsControl is bound to Guests which is ObservableCollection. Guests collection can have objects of two different types: USGuest and UKGuest, both inheriting from Guest. Is it possible to make two (or more) templates for ItemsControl and make it automatically choose between them depending on the run-time type of the current item in collection?
views:
54answers:
2I haven't tried this, but have you tried to set the ItemsSource to a ObservableCollection of Guest objects and the set the DataTemplate for both types?
<DataTemplate DataType="{x:Type my:USGuestViewModel}">
<my:USGuestView/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:UKGuestViewModel}">
<my:UKGuestView/>
</DataTemplate>
EDIT: 'my' is a declaration of the namespace where your ViewModels and Views live, so you should add something like this in the beggining of the xaml:
<UserControl x:Class="my.namespace.SuperView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:my.namespace">
I've checked and you cannot set two DataTemplates in the ItemTemplate property. But you can set them in your UserControl Resources Property:
<UserControl.Resources>
<DataTemplate DataType="{x:Type my:USGuestViewModel}">
<my:USGuestView/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:UKGuestViewModel}">
<my:UKGuestView/>
</DataTemplate>
</UserControl.Resources>
Sorry, I didn't mean to be a party pooper and not offer a solution. But this is one of the biggest hurdles I run into when using MVVM in Silverlight.
One thing I've done in the past is use a UserControl with just a ContentPresenter inside as the ItemsTemplate. (So many layers!) In the UserControl, when the DataContext changes, I would chose a template to use from the resources of the UserControl. (The templates would not actually have to be inside the UserControl, but I like that encapsulation the best.)
MainPage:
<UserControl>
<UserControl.Resources>
<DataTemplate x:key="itemTemplate">
<my:ItemView />
</DataTemplate>
</UserControl.Resources>
<ItemsControl ItemTemplate="{StaticResource itemTemplate}" />
</UserControl>
ItemView.xaml:
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="Template1">
<!-- Template #1 -->
</DataTemplate>
<DataTemplate x:Key="Template2">
<!-- Template #2 -->
</DataTemplate>
</UserControl.Resources>
<ContentPresenter Name="presenter"
Content="{Binding}" />
</UserControl>
ItemView.xaml.cs
...
OnDataContextChanged(...)
{
var content = this.DataContext as MyDataType;
DataTemplate template;
switch (content.State)
{
case State1:
template = this.Resources["template1"] as DataTemplate;
break;
case State2:
template = this.Resources["template2"] as DataTemplate;
break;
}
this.presenter.ContentTemplate = template;
}
...
And if you're still following along, note that Silverlight also doesn't provide an OnDataContextChanged method like you get in WPF. So, to cover that, see what Jeremy Likness says about it over here:
http://www.codeproject.com/Articles/38559/Silverlight-DataContext-Changed-Event.aspx
I use that pretty often. Thanks, Jeremy!
Also, there are some pretty severe limitations to this as well, when compared to all of the power that WPF gives you in that arena. For instance, there really is no good way do fake a ItemContainerStyle Selector. (That I know of.)