Saturday, January 15, 2011
Prism 4 Gems: Rendering Heterogeneous Collections with DataTemplateSelector
Prism 4 has a lot to offer in terms of its primary features, which includes UI Composition (Regions), Modularity, Pub/Sub events, Commands, Navigation, and MVVM guidance. But there are also a number of helpful little chunks of code included that you can use or copy even if you are not using the rest of Prism. One of those that I want to show you how to use in this post is the DataTemplateSelector class. DataTemplates in WPF have a very important feature: the DataType property that allows DataTemplates to be automatically applied to a data bound object based on its type. This feature is very similar to the implicit styles capability that WPF has had since the start that is a new feature in Silverlight 4. Unfortunately Silverlight is still lacking this feature. In Silverlight, you have to associated a single data template with a data bound control through its template property (ItemTemplate for ItemsControl and ContentTemplate for ContentControl). When you want to bind to a heterogeneous collection of objects (that all share a common base class or interface that the collection is defined in terms of), you are kind of stuck in Silverlight if you want to write specific templates per derived type. DataTemplateSelector from Prism comes to the rescue for this scenario however. The definition of DataTemplateSelector is kind of strange – it is a ContentControl derived class that amounts to a switch-case statement you can put in your XAML to select the right data template based on the actual type of the data object it is being rendered for. So as a simple example, say that you had some base class that a number of different derived classes were going to inherit from. Each derived class was going to define its own set of properties that need to be presented and bound to controls when that type is encountered in a collection of the base class type. So with a simple base type like the following: 1: public class DataTypeBase { }
We could define two derived types like the following:
1: public class DataType1 : DataTypeBase
2: {
3: public int ID { get; set; }
4: public string Name { get; set; }
5: }
6:
7: public class DataType2 : DataTypeBase
8: {
9: public int Index { get; set; }
10: public string Description { get; set; }
11: }
Then if we were going to present items of that type in a ListBox, ComboBox or other ItemsControl-based control, we would define the data templates we wanted to use to render them out:
1: <DataTemplate x:Key="DataType1">
2: <StackPanel Orientation="Horizontal">
3: <TextBlock Text="{Binding ID}"/>
4: <toolkit:Separator />
5: <TextBlock Text="{Binding Name}" />
6: </StackPanel>
7: </DataTemplate>
8:
9: <DataTemplate x:Key="DataType2">
10: <StackPanel Orientation="Horizontal">
11: <TextBox Text="{Binding Index}" />
12: <toolkit:Separator />
13: <TextBox Text="{Binding Description}" />
14: </StackPanel>
15: </DataTemplate>
This is all just simple, basic Silverlight (and WPF) data binding concepts so far.
Now say we want to expose a collection from a view model that allows us to put instances of both DataType1 and DataType2 in it. We would just define a collection in terms of the base class:
1: public class MainPageViewModel
2: {
3: public MainPageViewModel()
4: {
5: Data = new ObservableCollection<DataTypeBase>();
6: // Hard code some items for demo purposes
7: Data.Add(new DataType1 { ID = 42, Name = "Fred" });
8: Data.Add(new DataType2 { Index = 33, Description = "This is a different data type" });
9: }
10: public ObservableCollection<DataTypeBase> Data { get; set; }
11: }
Now the only problem in Silverlight is how to associate those data templates applied. If there were only going to be a single type or a single rendering of the objects in the bound control, you could just set a single data template through the ItemTemplate property:
1: <ListBox ItemsSource="{Binding Data}"
2: ItemTemplate="{StaticResource MyDataTemplate}" />
But we want both data templates defined above to be applied to their respective types, which will be in the bound collection. This is where the DataTemplateSelector comes in.
The way you use it is to define a DataTemplate (typically as a resource at the current time because of this bug that causes memory leaks if you define a DataTemplate nested under the element). Inside the DataTemplate, you create an instance of the DataTemplateSelector. Then you simply nest the multiple DataTemplates you want to apply by type inside the DataTemplateSelector’s resources:
1: <UserControl.Resources>
2: <DataTemplate x:Key="SelectorDataTemplate">
3: <prism:DataTemplateSelector Content="{Binding}"
4: HorizontalContentAlignment="Stretch"
5: IsTabStop="False">
6: <prism:DataTemplateSelector.Resources>
7: <DataTemplate x:Key="DataType1">
8: <StackPanel Orientation="Horizontal">
9: <TextBlock Text="{Binding ID}"/>
10: <toolkit:Separator />
11: <TextBlock Text="{Binding Name}" />
12: </StackPanel>
13: </DataTemplate>
14:
15: <DataTemplate x:Key="DataType2">
16: <StackPanel Orientation="Horizontal">
17: <TextBox Text="{Binding Index}" />
18: <toolkit:Separator />
19: <TextBox Text="{Binding Description}" />
20: </StackPanel>
21: </DataTemplate>
22:
23: </prism:DataTemplateSelector.Resources>
24: </prism:DataTemplateSelector>
25: </DataTemplate>
26: </UserControl.Resources>
You can see that because the DataTemplateSelector is a ContentControl, it can be the single root element of a DataTemplate, and an “instance” of it will be rendered out for each item in the collection it is used with. However, the DataTemplateSelector does not really have any visual appearance of its own. What it does is render out one of the DataTemplates defined in its Resources collection, selecting the appropriate one based on the data type of the object that is the DataContext.
So the steps are as follows:
- Define a DataTemplate in your Resources and give it a key.
- Declare a Prism DataTemplateSelector as the element of the DataTemplate.
- Set its Content by binding to the DataContext that flows into it (Content={Binding}).
- Add individual DataTemplates in the Resources collection of the DataTemplateSelector
- Set the x:Key for each nested DataTemplate to the Type name of the data object you want it to be used for.
- Set the root DataTemplate as the ItemTemplate on a control bound to a heterogeneous collection of those types.
Simple, straightforward, works great. You can download a full sample here.
Enjoy!
Friday, January 14, 2011
Upcoming Webinar: Querying and Updating Data in Silverlight with WCF RIA Services
On 2 Feb at 1000 PST, I will be doing a Webinar on the SilverlightShow site on using WCF RIA Services to query and update data. If you have been following my series on WCF RIA Services, this will parallel the content there closely, so acts as a nice visual complement to the written version. This session will attempt to cover everything in the first couple articles in a single hour(ish) session. I will be doing subsequent Webinars there about every month to cover the other aspects shown in the series and will probably show more than is covered in the written series. So check it out.
Cloud-based TFS with DiscountASP.net
This week I needed to get some source control set up for a new project with a customer. The work is going to be done by a small team of onsite and offsite developers. We started down the path of setting up a new TFS instance and then I decided to take a quick look into just using a cloud-hosted TFS service for the first time. I was extremely pleased to see a pretty cheap option with DiscountASP.net. It literally took less than 10 minutes of straightforward configuration and we were back to focusing on the product we need to build instead of setting up infrastructure to support the project. I just thought I would throw this out there to people who are considering different options for source control. The integrated experience of TFS is fantastic in VS 2008 and VS 2010. It also works wonderfully remotely because of the architecture of TFS once you get it set up. But it is a fair amount of work to get a TFS server stood up and exposed so it can be accessed anywhere. The time I saved not setting this up myself paid for at least the first year of hosting for the small team that we have. So if you have a small team and are looking to have great source control and work item integration that TFS provides but don’t want to have to set up the infrastructure for it yourself, you should definitely check out DiscountASP.net’s hosting.
|








| June, 2013 (1) |
| May, 2013 (2) |
| April, 2013 (2) |
| March, 2013 (2) |
| February, 2013 (2) |
| January, 2013 (2) |
| December, 2012 (3) |
| November, 2012 (1) |
| October, 2012 (1) |
| August, 2012 (2) |
| June, 2012 (2) |
| May, 2012 (3) |
| April, 2012 (1) |
| March, 2012 (2) |
| February, 2012 (2) |
| January, 2012 (1) |
| November, 2011 (4) |
| October, 2011 (1) |
| September, 2011 (2) |
| August, 2011 (1) |
| July, 2011 (1) |
| May, 2011 (5) |
| March, 2011 (4) |
| February, 2011 (2) |
| January, 2011 (3) |
| November, 2010 (4) |
| October, 2010 (1) |
| September, 2010 (5) |
| August, 2010 (5) |
| July, 2010 (6) |
| June, 2010 (8) |
| May, 2010 (2) |
| April, 2010 (2) |
| January, 2010 (1) |
| December, 2009 (3) |
| November, 2009 (2) |
| October, 2009 (3) |
| September, 2009 (3) |
| August, 2009 (2) |
| July, 2009 (3) |
| May, 2009 (3) |
| April, 2009 (2) |
| March, 2009 (1) |
| February, 2009 (2) |
| January, 2009 (2) |
| December, 2008 (1) |
| November, 2008 (2) |
| October, 2008 (5) |
| September, 2008 (4) |
| August, 2008 (2) |
| July, 2008 (1) |
| June, 2008 (2) |
| May, 2008 (2) |
| April, 2008 (3) |
| February, 2008 (6) |
| January, 2008 (3) |
| December, 2007 (1) |
| November, 2007 (1) |
| October, 2007 (5) |
| September, 2007 (1) |
| July, 2007 (3) |
| June, 2007 (8) |
| April, 2007 (2) |
| March, 2007 (4) |
| February, 2007 (1) |
| December, 2006 (2) |
| November, 2006 (9) |
| October, 2006 (5) |
| September, 2006 (3) |
| August, 2006 (2) |
| July, 2006 (4) |
| June, 2006 (5) |
| May, 2006 (10) |
| April, 2006 (4) |
| March, 2006 (2) |
| February, 2006 (12) |
| January, 2006 (7) |
| December, 2005 (2) |
| November, 2005 (15) |
| October, 2005 (6) |
| September, 2005 (7) |
| August, 2005 (3) |
| July, 2005 (10) |
| June, 2005 (11) |
| May, 2005 (7) |
| April, 2005 (8) |
| March, 2005 (6) |
| February, 2005 (2) |
| January, 2005 (6) |
| December, 2004 (3) |
| November, 2004 (5) |
| October, 2004 (2) |
| September, 2004 (5) |
| August, 2004 (13) |
| July, 2004 (6) |
| June, 2004 (14) |
| May, 2004 (17) |
| April, 2004 (12) |
| March, 2004 (8) |
| February, 2004 (10) |
| January, 2004 (14) |
| December, 2003 (9) |
| November, 2003 (13) |
| October, 2003 (3) |
Sign In
|