在理解ItemTemplate、ContentTemplate和DataTemplate的关系的以前,咱们先来看看ContentControl类和ItemsControl类。ContentControl类是内容控件的基类,如Button, CheckBox,最明显的特征就是这个控件有Content属性,有Content属性的系统控件都是ContentControl的子类。ItemsControl类是列表内容控件的基类,如ListBox,它和ContentControl类是相似的,只不过ContentControl类是单项的内容,ItemsControl是多项的内容。html
那么全部继承自ContentControl的内容控件的ContentTemplate属性和全部继承自ItemsControl的列表控件的ItemTemplate属性,都是DataTemplate类型的,意思就是咱们能够经过DataTemplate来定义ContentControl和ItemsControl的控件的UI效果和数据的显示。编程
DataTemplate是一种可视化的数据模板,它强大的做用在于能够把数据经过绑定的方式展示到控件上。在上面的例子中,咱们介绍了用DataTemplate去实现了UI控件的内容的显示,那么其实DataTemplate最主要的做用并非去取代ControlTemplate的样式定义,而是经过数据绑定把数据的控件的数据源的信息展示到控件上。app
下面咱们仍是经过一个Button的控件来看一下DataTemplate的数据绑定是如何发挥做用的。字体
代码清单2-5:数据模板(源代码:第2章\Examples_2_5)spa
(1)首先定义一个Person类表示是数据实体的类型,代码以下:设计
public class Person { public string LastName { get; set; } public string FirstName { get; set; } }
(2)设计一个DataTemplate,并把这个DataTemplate做为一个资源来使用,这是和Style资源是同样的道理,DataTemplate也能够做为公共的资源给多个控件去使用。那么这个模板的内容是使用StackPanel控件把Person对象的信息水平排列起来。code
< Page.Resources> <DataTemplate x:Key="PersonNameDataTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text=", "/> <TextBlock Text="{Binding FirstName}"/> </StackPanel> </DataTemplate> </ Page.Resources>
(3)建立一个Button控件,把ContentTemplate属性和模板资源关联起来。htm
<Button x:Name="singlePersonButton" ContentTemplate="{StaticResource PersonNameDataTemplate}"/>
(4)建立一个Person对象而且赋值给Button控件的Content属性。对象
singlePersonButton.Content = new Person { FirstName = "lee", LastName = "Terry" };blog
最后咱们能够看到按钮的运行效果如图2.14所示,DataTemplate能够把数据对象绑定起来来实现更加灵活的通用的强大的UI数据显示效果。
图2.14 数据模板绑定的按钮
那么刚才的示例是DataTemplate在ContentControl类型的控件上的应用,那么下面咱们再来看看DataTemplate在ItemsControl类型的控件上的实现,ContentControl和ItemsControl也是能够直接做为控件去使用的,若是咱们并不须要Button或者ListBox这些控件的一些高级功能,就能够直接使用ContentControl或者ItemsControl控件。
(1)定义一个ItemsControl控件,把ItemTemplate属性和模板资源关联起来。
<ItemsControl x:Name="itemsControl" ItemTemplate="{StaticResource PersonNameDataTemplate}" />
(2)建立一个Person对象的集合而且赋值给ItemsControl控件的ItemsSource属性。
Persons.Add(new Person { FirstName = "lee2", LastName = "Terry2" });
Persons.Add(new Person { FirstName = "lee3", LastName = "Terry3" });
Persons.Add(new Person { FirstName = "lee4", LastName = "Terry4" });
Persons.Add(new Person { FirstName = "lee5", LastName = "Terry5" });
itemsControl.ItemsSource = Persons;
这时候能够看到运行效果如图2.15所示,ItemsControl能够把数据集合经过列表的形式展示出来,可是你会发现直接用ItemsControl实现的列表的功能很是有限,而且也不能滚动,接下来再结合一下ContentTemplate来进行完善这个列表的控件。
图2.15 数据模板绑定的列表
(3)定义一个ItemsControl的样式,其实就是自定义一个ControlTemplate的模板做为ItemsControl控件的模板来使用,那么这个模板就是一个内容的展示形式的模板。咱们在ControlTemplate模板上定义了一个ScrollViewer控件而后里面再使用了一个StackPanel控件,最里面的是ItemsPresenter控件。列表的DataTemplate的显示内容就是直接投影在ItemsPresenter控件上面的。咱们对ScrollViewer控件和StackPanel控件都设置了不一样的边框颜色,这样在运行的时候就能够很明显地看出来控件之间的关系是怎样的。
<Style x:Name="ItemsControlStyle" TargetType="ItemsControl"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ItemsControl"> <ScrollViewer BorderBrush="Red" BorderThickness="6"> <StackPanel Orientation="Horizontal" Background="Blue"> <Border BorderBrush="Yellow" BorderThickness="3"> <ItemsPresenter /> </Border> </StackPanel> </ScrollViewer> </ControlTemplate> </Setter.Value> </Setter> </Style>
(4)在ItemsControl上添加Style属性为上面定义的样式。
<ItemsControl x:Name="itemsControl" ItemTemplate="{StaticResource PersonNameDataTemplate}" Style="{StaticResource ItemsControlStyle}"/>
程序的运行效果如图2.16所示。
图2.16 列表控件的各个模块
对于系统的样式,咱们能够经过在C#代码里面读取出来而后再修改,实现动态更换主题的目的。那么技术老是相通的,对于控件的DataTemplate,咱们同样也能够经过C#代码去读取出来,而后动态地更换,实现更加丰富和灵活化的样式展现方案。在C#代码里面读取和更换数据模板也是经过对ContentTemplate属性进行读取和赋值就能够了。
这种读取和更换数据模板在列表的控件中会比较常见,好比我要实现一个这样一个功能,经过一个列表展示出一批数据,用户打击某一条数据的时候,这条数据的样式要发生改变,表示选取了这条数据,而后用户能够取消这条数据的选择也能够继续选择多条数据。那么这样的功能在数据多选的状况下是很是广泛的功能来的。下面咱们来实现这样的一个功能。
代码清单2-6:动态更换样式(源代码:第2章\Examples_2_6)
(1)定义3个DataTemplate资源,一个是非选中状态,一个是选中状态的,还有一个是默认的状态,其实默认的状态和非选中状态是同样的,可是由于默认的状态的数据项样式不能在C#里面再次调用。在两个模板中都添加了Tap事件,用户捕获用户的点击事件。数据源集合与上一个例子同样。
< Page.Resources> <!--选中数据项的样式--> <DataTemplate x:Key="dataTemplateSelectKey" x:Name="dataTemplateSelectName"> <Grid Tapped="StackPanel_Tap_1" Background="Red"> <TextBlock Text="{Binding LastName}" FontSize="50" /> </Grid> </DataTemplate> <!--默认数据项的样式,注意默认的数据项样式不能在C#中再次调用--> <DataTemplate x:Key="dataTemplateDefaultKey" x:Name=" dataTemplateDefaultName"> <StackPanel Orientation="Horizontal" Tapped ="StackPanel_Tap_1"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text=", "/> <TextBlock Text="{Binding FirstName}"/> </StackPanel> </DataTemplate> <!--非选中数据项的样式--> <DataTemplate x:Key="dataTemplateNoSelectKey" x:Name="dataTemplateNoSelectName"> <StackPanel Orientation="Horizontal" Tapped ="StackPanel_Tap_1"> <TextBlock Text="{Binding LastName}"/> <TextBlock Text=", "/> <TextBlock Text="{Binding FirstName}"/> </StackPanel> </DataTemplate> </ Page.Resources> ……省略若干代码 //建立ItemsControl控件来绑定列表的数据 <ItemsControl x:Name="listbox" ItemTemplate="{StaticResource dataTemplateDefaultKey }"/>
(2)处理点击事件,判断当前控件的模板和从新赋值模板。能够经过Name属性访问XAML中定义的DataTemplate。
private void StackPanel_Tap_1(object sender, TappedRoutedEventArgs e) { // 获取ItemsControl对象的ItemContainerGenerator属性 // 经过点击的控件的DataContext判断所绑定的数据对象 // 而后从ItemContainerGenerator里面获取到当前的ContentPresenter对象 ContentPresenter myContentPresenter = (ContentPresenter)(listbox.ItemContainerGenerator.ContainerFromItem((sender as Panel).DataContext)); // 判断数据模板是选中状态的仍是非选中状态的,而后进行赋值 if (myContentPresenter.ContentTemplate.Equals(dataTemplateSelectName)) { //赋值非选中状态的模板 myContentPresenter.ContentTemplate = dataTemplateNoSelectName; } else { //赋值选中状态的模板 myContentPresenter.ContentTemplate = dataTemplateSelectName; } }
运行的效果如图2.17所示,当咱们点击一下数据项的时候,字体会变大,背景会变成红色,在点击一次就会变成原来的样子。
图2.17 动态更换样式
(3)在这里还要注意一点的是,若是使用的时ListBox控件而不是ItemsControl控件的时候,在获取ContentPresenter对象的时候须要经过可视化树去查找。代码的实现以下所示:
private void StackPanel_Tap_1(object sender, TappedRoutedEventArgs e) { //获取到的对象是ListBoxItem ListBoxItem myListBoxItem = (ListBoxItem)(listbox.ItemContainerGenerator.ContainerFromItem((sender as Panel).DataContext)); // 在ListBoxItem中查找ContentPresenter ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem); ……//省略若干代码 } //查找可视化树某个类型的元素 private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is childItem) return (childItem)child; else { childItem childOfChild = FindVisualChild<childItem>(child); if (childOfChild != null) return childOfChild; } } return null; }
本文来源于《深刻理解Windows Phone 8.1 UI控件编程》
源代码下载:http://vdisk.weibo.com/s/zt_pyrfNHoezI
欢迎关注个人微博@WP林政
WP8.1技术交流群:372552293