在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的關係。若是你以爲本文哪裏有說的不對的地方,歡迎指正!感謝閱讀!