WPF 的 ControlTemplate 與 DataTemplate

ControlTemplate 和 DataTemplate 看起來都會左右控制項的外觀,這兩種樣板到底差在哪裡?

Stackoverflow 有個回答很簡潔:「a ControlTemplate describes how to display a Control while a DataTemplate describes how to display Data.」亦即 ControlTemplate 描述如何顯示控制項,DataTemplate 描述如何顯示資料。可是,這又是什麼意思呢?聽起來還是霧煞煞。

不妨做個小實驗,用 Visual Studio 建立一個 WPF 應用程式專案,然後把下圖中的 XAML 逐字敲進去,就能在 Visual Studio 的 XAML Designer 中觀察 UI 的變化:


如果有自己動手實驗上面的程式碼,並嘗試做些修改,應該就不太需要看底下的冗長解說了。(謎之音:其實比較像繞口令 XD)

在這個範例當中,我把所有的樣板都定義在 <Windows.Resources> 裡面,也就是把它們當成資源,好讓各控制項重複使用這些樣板。

CheckBox1  透過 Template 屬性指定要套用一個叫做 "simpleTemplate" 的 ControlTemplate。此控制項樣板只包含一個 <ContentPresenter>,用來決定控制項的內容要如何呈現。在指定 ContentPresenter 的 Content 屬性時,使用了 TemplateBinding 標記延伸語法來指定其內容來源是 "Content",也就是此 CheckBox 控制項的 Content 屬性。所以實際呈現的結果就是單純的文字:"CheckBox 1"。注意它沒有核取方塊。

CheckBox2 套用了另一個 ControlTemplate,叫做 "myCheckBoxTemplate"。此樣板定義的控制項外觀是在最外層有一個 Border 控制項(會產生一個紅色框),裡面包著一個 CheckBox,然後再於其中定義 ContentPresenter,繫結至控制項的 Content 屬性。

CheckBox3 僅套用 DataTemplate(透過 ContentTemplate 屬性),沒有使用控制項樣板。所以從執行的結果可以看到,它沒有紅色外框。注意 Content 屬性雖然有指定為 "CheckBox 3",但是沒有作用,因為我們同時指定了資料樣板,所以其內容最終是由資料樣板來決定。

CheckBox4 同時套用了 ControlTemplate 和 DataTemplate,所以最終的顯示結果是兩者的組合:
  • Template 屬性指定使用控制項樣板 "myCheckBoxTemplate" 來決定控制項的外觀。因此,控制項的外觀會有控制項樣板所定義的紅色方框以及方框裡面的核取方塊。此樣板的 ContentPresenter 指定要使用控制項的 ContentTemplate 屬性作為資料來源。所以內容的呈現將由控制項的 ContentTemplate 屬性決定。
  • 控制項的 ContentTemplate 屬性指定使用 myDataTemplate 來決定控制項的內容。因此,剛才的紅色方框加上核取方塊,再加上資料樣板中的兩個按鈕,便組合成了最終的顯示結果。

做完實驗之後,再回頭看這句話:「ControlTemplate 描述如何顯示控制項,DataTemplate 描述如何顯示資料。」應該就不會覺得那麼抽象模糊了吧!

最後附上程式碼:
<Window x:Class="ControlTemplateDemo.CheckBoxDemo"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="CheckBoxDemo" Height="163" Width="300">
    <Window.Resources>
        <ControlTemplate x:Key="simpleTemplate" TargetType="ContentControl">
            <ContentPresenter 
                    Content="{TemplateBinding Content}"
                    ContentTemplate="{TemplateBinding ContentTemplate}">
            </ContentPresenter>
        </ControlTemplate>

        <ControlTemplate x:Key="myCheckBoxTemplate" TargetType="CheckBox">
            <Border Margin="4" Padding="4" BorderThickness="1" BorderBrush="Red" >
                <CheckBox>
                    <ContentPresenter 
                    Content="{TemplateBinding Content}"
                    ContentTemplate="{TemplateBinding ContentTemplate}">
                    </ContentPresenter>
                </CheckBox>
            </Border>
        </ControlTemplate>

        <DataTemplate x:Key="myDataTemplate">
            <StackPanel Orientation="Horizontal">
                <Button Content="OK" Margin="4" />
                <Button Content="Cancel" Margin="4"  />
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <CheckBox Content="CheckBox 1" Template="{StaticResource simpleTemplate}" />
            <CheckBox Content="CheckBox 2" Template="{StaticResource myCheckBoxTemplate}" />
            <CheckBox Content="CheckBox 3" ContentTemplate="{StaticResource myDataTemplate}" />
            <CheckBox Content="Check Box 4" 
                  Template="{StaticResource myCheckBoxTemplate}" 
                  ContentTemplate="{StaticResource myDataTemplate}">
            </CheckBox>
        </StackPanel>
    </Grid>
</Window>

延伸閱讀

沒有留言:

技術提供:Blogger.
回頂端⬆️