在wp/silverlight/wpf也会常常看到控件模板。常常混淆的应该是DataTemplate和ControlTemplate,本篇文章就来谈谈两件衣服DataTemplate和ControlTemplate的关系。本篇文章主要会以wpf控件为主,以便最后的源码你们均可以打开。算法
在开始以前,咱们先去看一下ContentControl的定义,不管在wp仍是在wpf中其都有下面两个属性:
public object Content { get; set; }
public DataTemplate ContentTemplate { get; set; }spa
其特色是只能容纳一个内容,内容类型是object类型,其中Button控件是咱们你们比较熟悉且属于ContentControl的类,下面咱们看一下直接使用TextBlock做为其内容,Button会工做的很好。以下图:3d
这个很正常,由于内容是object的嘛,那么下面我就使用另一种笔刷做为其内容。看结果:code
显示内容成了笔刷转化后的字符串。若是是在回头看看DataTemplate的话,会发现其摘要是:orm
获取或设置用于显示 System.Windows.Controls.ContentControl 内容的数据模板。内容的数据模板也就是说内容以什么样子表现出来。blog
下面我就让button的内容-笔刷在一个圆上显示出来。递归
以上我使用了button内容控件是实现了用圆形来展现button的内容。固然推而广之,我可使用任何内容控件,先设置其Content(该内容能够是任何复杂的内容),而后使用DataTemplate来表达Content的数据。下面咱们就使用一个UserControl控件来实现一个同窗的信息:定义一个Student类,而后初始化一个stu,设置其为UC的Content,而后罗列出Content中的数据。图片
由上面的两个例子能够得出的结论是ContentControl中的DataTemplate是用来表示Content中的数据的,也就是说Content是DataTemplate的绑定的源,具体的表现形式是由DataTemplate决定的。ip
在Control中,有个Template属性,其摘要和返回结果以下:字符串
// 摘要:
// 获取或设置控件模板。
// 返回结果:
// 用于定义 System.Windows.Controls.Control 的外观的模板。
public ControlTemplate Template { get; set; }
和DataTemplate不同的是:该控件定义外观模板。咱们还以Button为例子吧。上面返回结果说了是外观的模板,那我想要一个圆角的Button,应该属于外观的范畴了,很快我想到了使用Border.下面就开工吧。我先弄个按钮,给他写上内容和加上背景颜色:
而后加上Template属性,结果发现背景颜色和内容都没有了。
若是是按照这样的写法,上面的结果能够看到Content没有abc了。为了显示出来abc我是否是能够在Border里面加个控件TextBlock,而后在 TextBlock上面写上几个字母,发现能够显示了,可是若是是写的不是abc,仍是不能显示abc,说明如今显示的内容的决定于TextBlock,若是能有一种绑定的话多好呢,我就可让TextBlock显示的和abc的一致了。有个TemplateBinding,使用时要在ControlTemplate标签中使用Target。效果以下:
虽然效果实现了,可是有个很严重的问题是咱们的Button的Content是object类型,Text是一个字符串类型。若是是button什么时间心情很差,Content属性变成Image了,那是否是我要跟着“Button”受气呢?为了避免受气,想一下有没有使用于全部类型内容的容器呢?答案是确定的。使用ContentPresenter。如今不管你是图片仍是文字,我都不用鸟你,都有ContentPresenter照着。下面亮出来他的样子
ContentPresenter很是标准,他会为你自动匹配Target的Content和ContentTemplate。若是Button.Content有关的属性(如FontSize,Foreground,FontFamily等)发生变化了,不用作任何更改ContentPresenter会帮咱们处理的很好,可是若是是和Button自己有关的属性,(如背景色等),须要显式的调整。
在上面的例子中使用ContentPresenter时,会发现其也有一个ContentTemplate,是否是会猜出来是DataTemplate类型的,并且是在Template树上"长着"。由此能够猜测ContentTemplate对应的DataTemplate是Template对应的ControlTemplate树上的一棵子树。为了证实这个事实,下面我还以Button来讲明。
如今我分别在button里面使用文字,图片,笔刷做为button的内容,而后添加一个容器来显示Template的子树,下面是xaml代码:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Button Grid.Row="0" Grid.Column="0" Content="Click to Dump" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick" /> <Button Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick"> <Image Source="Images/vs.png" Stretch="None" /> </Button> <Button Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick"> <Button.Content> <RadialGradientBrush> <GradientStop Offset="0" Color="Blue" /> <GradientStop Offset="1" Color="AliceBlue" /> </RadialGradientBrush> </Button.Content> <Button.ContentTemplate> <DataTemplate> <Ellipse Width="100" Height="100" Fill="{Binding}" /> </DataTemplate> </Button.ContentTemplate> </Button> <ScrollViewer Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Auto"> <StackPanel Name="stackPanel" /> </ScrollViewer> </Grid>
我能够根据可视树的查找(VisualTreeHelper类提供的方法结合递归算法)来查看Button中的Template下面都有哪些子树。下面代码是后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } void OnButtonClick(object sender, RoutedEventArgs args) { Button btn = sender as Button; stackPanel.Children.Clear(); DumpVisualTree(btn, 0); } void DumpVisualTree(DependencyObject parent, int indent) { TextBlock txtblk = new TextBlock(); txtblk.Text = String.Format("{0}{1}", new string(' ', 4 * indent), parent.GetType().Name); stackPanel.Children.Add(txtblk); int numChildren = VisualTreeHelper.GetChildrenCount(parent); for (int childIndex = 0; childIndex < numChildren; childIndex++) { DependencyObject child = VisualTreeHelper.GetChild(parent, childIndex); DumpVisualTree(child, indent + 1); } } }
分别点击各个按钮,能够看到各个按钮的Template是怎么构造的,有个共同的特色可视树都包含有ContentPresenter,这不正说明了DataTemplate被ContentPresenter替代掉了,说明的是DataTemplate生成的是ContentPresenter如下的树(wp和silverlight中ContentPresenter如下的树可能和wpf上面有些不同)。也验证了DataTemplate是ControlTemplate的子树的一部分。
本文主要经过介绍DataTemplate和ControlTemplate,而后引入ContentPresenter,经过可视树的帮助类VisualTreeHelper类查看控件所包含的模板内容,进而验证了DataTemplate和ControlTemplate的关系。若是你以为本文哪里有说的不对的地方,欢迎指正!感谢阅读!