在这篇文章中,我将要介绍在Windows Phone 7开发环境中怎么去创建一个DataTemplateSelector的虚类和自定义一个CustomDataTemplateSelector。通常DataTemplateSelector提供一种基于数据对象和数据绑定的选择DataTemplate的途径。特别是在对于同类的对象有多个DataTemplate,并且能根据逻辑选择DataTemplate应用到数据对象上。
简单的说,DataTemplateSelector允许我们编写一些逻辑去选择哪个DataTemplate运用到哪个特定的项上。甚至可以创建一个新的Template 。
NOTE: DataTemplateSelector 在WPF中是一个很好用的类,不过它现在还没有Silverlight版本
下面是一个在dev forums很热门的一个问题:
"I have a list of different types of elements. I would like to display a different data template for different list elements, based on what type it is. "
这个问题的答案是利用DataTemplateSelector。因此在这篇文章里我先要解释怎么去实现一个DataTemplateSelector,然后我会介绍怎么去实现自己的自定义CustomDataTemplateSelector 。最后的效果图会贴在本文中。
有很多的方法去建立一个动态的DataTemplateSelector 。你可以从WPF默认实现DataTemplateSelector的代码中去提取代码,利用ValueConverter实现等等。在这篇文章中我会介绍怎么去建立一个继承自ContentControl(这里把ContentControl当做一个基类) 的DataTemplateSelector 。
我们要做的第一件事就是建立一个带有选择合适Template虚方法抽象类。这个虚方法SelectTemplate可以根据优先属性的值(在一个继承自DataTemplateSelector特定的类被重写时)返回一个合适的Template。这里需要重写基类的OnContentChanhed 事件,下面是源码:
public abstract class DataTemplateSelector : ContentControl { public virtual DataTemplate SelectTemplate(object item, DependencyObject container) { return null; } protected override void OnContentChanged(object oldContent, object newContent) { base.OnContentChanged(oldContent, newContent); ContentTemplate = SelectTemplate(newContent, this); } }
建立一个自定义的CustomDataTemplateSelector
为了建立一个自定义的模板选择器,首先需要建立一个继承自DataTemplateSelector 的类,然后要override它的SelectTemplate方法。一旦你的类定义好之后,你就可以把这个类的实例指定给你的界面element的template selector property。
为了演示,这里我会建立一个FoodTemplateSelector 类,它包含三个不同的DataTemplates:Healthy, UnHealthy and NotDetermined。在SelectTemplate 方法里我会加上一些条件,这些条件可以根据 data context的属性值选择合适的DataTemplate。基本上我们可以通过数据源的属性值选择正确的template。
这个FoodTemplateSelector 的源码如下:
public class FoodTemplateSelector : DataTemplateSelector { public DataTemplate Healthy { get; set; } public DataTemplate UnHealthy { get; set; } public DataTemplate NotDetermined { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { Data foodItem = item as Data; if (foodItem != null) { if (foodItem.Type == "Healthy") { return Healthy; } else if (foodItem.Type == "NotDetermined") { return NotDetermined; } else { return UnHealthy; } } return base.SelectTemplate(item, container); } }
这里是数据类:
public class Data { public string Name { get; set; } public string Description { get; set; } public string IconUri { get; set; } public string Type { get; set; } }
这里为了表明FoodTemplateSelector的用法,使用了一个数据绑定的ListBox。这里是绑定的源码:
public MainPage() { InitializeComponent(); List<Data> list = new List<Data>(); Data item0 = new Data() { Name = "Tomato", IconUri = "Images/Tomato.png", Type = "Healthy" }; Data item1 = new Data() { Name = "Beer", IconUri = "Images/Beer.png", Type = "NotDetermined" }; Data item2 = new Data() { Name = "Fries", IconUri = "Images/fries.png", Type = "Unhealthy" }; Data item3 = new Data() { Name = "Sandwich", IconUri = "Images/Hamburger.png", Type = "Unhealthy" }; Data item4 = new Data() { Name = "Ice-cream", IconUri = "Images/icecream.png", Type = "Healthy" }; Data item5 = new Data() { Name = "Pizza", IconUri = "Images/Pizza.png", Type = "Unhealthy" }; Data item6 = new Data() { Name = "Pepper", IconUri = "Images/Pepper.png", Type = "Healthy" }; list.Add(item0); list.Add(item1); list.Add(item2); list.Add(item3); list.Add(item4); list.Add(item5); list.Add(item6); this.listBox.ItemsSource = list; }
下一步要做的是建立三个DataTemplates 并且把它们分别设定成ListBox的一个ItemTemplate。因为这三个DataTemplate都是独立的相互之间没有联系,这意味着你可以在每个DataTemplate上使用任何的element。因此一个数据源可以三种不同的显示。
左边的是效果图,这是这三个DataTemplates 的模样:
建立 ListBox的源码如下:
<ListBox x:Name="listBox" HorizontalContentAlignment="Stretch"> <ListBox.ItemTemplate> <DataTemplate> <local:FoodTemplateSelector Content="{Binding}"> <local:FoodTemplateSelector.Healthy> <DataTemplate> <StackPanel Orientation="Horizontal" Background="YellowGreen" Width="400" Margin="10"> <Image Source="{Binding IconUri}" Stretch="None"/> <TextBlock Text="{Binding Name}" FontSize="40" Foreground="Black" Width="280"/> <TextBlock Text="healty" /> </StackPanel> </DataTemplate> </local:FoodTemplateSelector.Healthy> <local:FoodTemplateSelector.UnHealthy> <DataTemplate> <Border BorderBrush="Red" BorderThickness="2" Width="400" Margin="10"> <StackPanel Orientation="Horizontal"> <Image Source="{Binding IconUri}" Stretch="None"/> <TextBlock Text="{Binding Name}" FontSize="40" Width="280"/> <Image Source="Images/attention.png" Stretch="None" Margin="10,0,0,0"/> </StackPanel> </Border> </DataTemplate> </local:FoodTemplateSelector.UnHealthy> <local:FoodTemplateSelector.NotDetermined> <DataTemplate> <StackPanel Orientation="Horizontal" Background="Gray" Width="400" Margin="10"> <Image Source="{Binding IconUri}" Stretch="None"/> <TextBlock Text="{Binding Name}" FontSize="40" Width="280"/> <Image Source="Images/question.png" Stretch="None" Margin="10,0,0,0"/> </StackPanel> </DataTemplate> </local:FoodTemplateSelector.NotDetermined> </local:FoodTemplateSelector> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
最终的结果:ListBox的每一个item由于不同的Type属性值都有不同的template 。
这里贴出有用的链接,提供了实现DataTemplateSelector 的途径: