DataTemplate和ControlTemplate的關係

在wp/silverlight/wpf也會常常看到控件模板。常常混淆的應該是DataTemplate和ControlTemplate,本篇文章就來談談兩件衣服DataTemplate和ControlTemplate的關係。本篇文章主要會以wpf控件爲主,以便最後的源碼你們均可以打開。算法

1、ContentControl中的DataTemplate

在開始以前,咱們先去看一下ContentControl的定義,不管在wp仍是在wpf中其都有下面兩個屬性:
public object Content { get; set; }
public DataTemplate ContentTemplate { get; set; }spa

其特色是隻能容納一個內容,內容類型是object類型,其中Button控件是咱們你們比較熟悉且屬於ContentControl的類,下面咱們看一下直接使用TextBlock做爲其內容,Button會工做的很好。以下圖:3d

image

這個很正常,由於內容是object的嘛,那麼下面我就使用另一種筆刷做爲其內容。看結果:code

image

顯示內容成了筆刷轉化後的字符串。若是是在回頭看看DataTemplate的話,會發現其摘要是:orm

獲取或設置用於顯示 System.Windows.Controls.ContentControl 內容的數據模板。內容的數據模板也就是說內容以什麼樣子表現出來。blog

下面我就讓button的內容-筆刷在一個圓上顯示出來。遞歸

image

以上我使用了button內容控件是實現了用圓形來展現button的內容。固然推而廣之,我可使用任何內容控件,先設置其Content(該內容能夠是任何複雜的內容),而後使用DataTemplate來表達Content的數據。下面咱們就使用一個UserControl控件來實現一個同窗的信息:定義一個Student類,而後初始化一個stu,設置其爲UC的Content,而後羅列出Content中的數據。圖片

image

由上面的兩個例子能夠得出的結論是ContentControl中的DataTemplate是用來表示Content中的數據的,也就是說Content是DataTemplate的綁定的源,具體的表現形式是由DataTemplate決定的。ip

2、Control的ControlTemplate

在Control中,有個Template屬性,其摘要和返回結果以下:字符串

// 摘要:
//     獲取或設置控件模板。
// 返回結果:
//     用於定義 System.Windows.Controls.Control 的外觀的模板。
public ControlTemplate Template { get; set; }

和DataTemplate不同的是:該控件定義外觀模板。咱們還以Button爲例子吧。上面返回結果說了是外觀的模板,那我想要一個圓角的Button,應該屬於外觀的範疇了,很快我想到了使用Border.下面就開工吧。我先弄個按鈕,給他寫上內容和加上背景顏色:

image

而後加上Template屬性,結果發現背景顏色和內容都沒有了。

image
若是是按照這樣的寫法,上面的結果能夠看到Content沒有abc了。爲了顯示出來abc我是否是能夠在Border裏面加個控件TextBlock,而後在 TextBlock上面寫上幾個字母,發現能夠顯示了,可是若是是寫的不是abc,仍是不能顯示abc,說明如今顯示的內容的決定於TextBlock,若是能有一種綁定的話多好呢,我就可讓TextBlock顯示的和abc的一致了。有個TemplateBinding,使用時要在ControlTemplate標籤中使用Target。效果以下:

image

雖然效果實現了,可是有個很嚴重的問題是咱們的Button的Content是object類型,Text是一個字符串類型。若是是button什麼時間心情很差,Content屬性變成Image了,那是否是我要跟着「Button」受氣呢?爲了避免受氣,想一下有沒有使用於全部類型內容的容器呢?答案是確定的。使用ContentPresenter。如今不管你是圖片仍是文字,我都不用鳥你,都有ContentPresenter照着。下面亮出來他的樣子

image

ContentPresenter很是標準,他會爲你自動匹配Target的Content和ContentTemplate。若是Button.Content有關的屬性(如FontSize,Foreground,FontFamily等)發生變化了,不用作任何更改ContentPresenter會幫咱們處理的很好,可是若是是和Button自己有關的屬性,(如背景色等),須要顯式的調整。

3、DataTemplate和ControlTemplate聯繫

在上面的例子中使用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); 
           } 
       } 
   } 

image

分別點擊各個按鈕,能夠看到各個按鈕的Template是怎麼構造的,有個共同的特色可視樹都包含有ContentPresenter,這不正說明了DataTemplate被ContentPresenter替代掉了,說明的是DataTemplate生成的是ContentPresenter如下的樹(wp和silverlight中ContentPresenter如下的樹可能和wpf上面有些不同)。也驗證了DataTemplate是ControlTemplate的子樹的一部分。

4、總結

本文主要經過介紹DataTemplate和ControlTemplate,而後引入ContentPresenter,經過可視樹的幫助類VisualTreeHelper類查看控件所包含的模板內容,進而驗證了DataTemplate和ControlTemplate的關係。若是你以爲本文哪裏有說的不對的地方,歡迎指正!感謝閱讀!

源碼下載:http://files.cnblogs.com/lzhp/TemlateDemo.zip

相關文章
相關標籤/搜索