DataTemplate和ControlTemplate的關系


在wp/silverlight/wpf也會經常看到控件模板。經常混淆的應該是DataTemplate和ControlTemplate,本篇文章就來談談兩件衣服DataTemplate和ControlTemplate的關系。本篇文章主要會以wpf控件為主,以便最后的源碼大家都可以打開。

一、ContentControl中的DataTemplate

在開始之前,我們先去看一下ContentControl的定義,無論在wp還是在wpf中其都有下面兩個屬性:
public object Content { get; set; }
public DataTemplate ContentTemplate { get; set; }

其特點是只能容納一個內容,內容類型是object類型,其中Button控件是我們大家比較熟悉且屬於ContentControl的類,下面我們看一下直接使用TextBlock作為其內容,Button會工作的很好。如下圖:

image

這個很正常,因為內容是object的嘛,那么下面我就使用另外一種筆刷作為其內容。看結果:

image

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

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

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

image

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

image

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

二、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本身有關的屬性,(如背景色等),需要顯式的調整。

三、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的子樹的一部分。

四、總結

本文主要通過介紹DataTemplate和ControlTemplate,然后引入ContentPresenter,通過可視樹的幫助類VisualTreeHelper類查看控件所包含的模板內容,進而驗證了DataTemplate和ControlTemplate的關系。如果你覺得本文哪里有說的不對的地方,歡迎指正!感謝閱讀!

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM