原文地址 http://www.cnblogs.com/zhouyinhui/archive/2007/03/28/690993.htmlhtml
WPF中的ControlTemplate(控件模板)
周銀輝
WPF包含數據模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,這裏討論一下ControlTemplate。
其實WPF的每個控件都有一個默認的模板,該模板描述了控件的外觀以及外觀對外界刺激所作出的反應。咱們能夠自定義一個模板來替換掉控件的默認模板以便打造個性化的控件。
與Style不一樣,Style只能改變控件的已有屬性值(好比顏色字體)來定製控件,但控件模板能夠改變控件的內部結構(VisualTree,視覺樹)來完成更爲複雜的定製,好比咱們能夠定製這樣的按鈕:在它的左辦部分顯示一個小圖標而它的右半部分顯示文本。
要替換控件的模板,咱們只須要聲明一個ControlTemplate對象,並對該ControlTemplate對象作相應的配置,而後將該ControlTemplate對象賦值給控件的Template屬性就能夠了。
ControlTemplate包含兩個重要的屬性:
1,VisualTree,該模板的視覺樹,其實咱們就是使用這個屬性來描述控件的外觀的
2,Triggers,觸發器列表,裏面包含一些觸發器Trigger,咱們能夠定製這個觸發器列表來使控件對外界的刺激發生反應,好比鼠標通過時文本變成粗體等。
參考如下代碼
學習

<Button>

<Button.Template>

<ControlTemplate>

<!--定義視覺樹-->

<Grid>

<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>

<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />

</Grid>

<!--定義視覺樹_end-->

</ControlTemplate>

</Button.Template>

</Button>
在上面的代碼中,咱們修改了Button的Template屬性,咱們定義了一個ControlTemplate,在<ControlTemplate> ... </ControlTemplate>之間包含的是模板的視覺樹,也就是如何顯示控件的外觀,咱們這裏使用了一個Ellipse(橢圓)和一個TextBlock(文本塊)來定義控件的外觀。
很容易聯想到一個問題:控件(Button)的一些屬性,好比高度、寬度、文本等如何在新定義的外觀中表現出來呢?
咱們使用TemplateBinding 將控件的屬性與新外觀中的元素的屬性關聯起來Width="{TemplateBinding Button.Width}" ,這樣咱們就使得橢圓的寬度與按鈕的寬度綁定在一塊兒而保持一致,同理咱們使用Text="{TemplateBinding Button.Content}"將TextBlock的文本與按鈕的Content屬性綁定在一塊兒。
除了定義控件的默認外觀外,也許咱們想還定義當外界刺激咱們的控件時,控件外觀作出相應的變化,這是咱們須要觸發器。參考如下代碼:
字體

<Button Content="test btn" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" >

<Button.Template>

<ControlTemplate>

<!--定義視覺樹-->

<Grid>

<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>

<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />

</Grid>

<!--定義視覺樹_end-->

<!--定義觸發器-->

<ControlTemplate.Triggers>

<Trigger Property="Button.IsMouseOver" Value="True">

<Setter Property="Button.Foreground" Value="Red" />

</Trigger>

</ControlTemplate.Triggers>

<!--定義觸發器_End-->

</ControlTemplate>

</Button.Template>

</Button>
在上面的代碼中注意到<ControlTemplate.Triggers>... </ControlTemplate.Triggers>之間的部分,咱們定義了觸發器 <Trigger Property="Button.IsMouseOver" Value="True">,其表示當咱們Button的IsMouseIOver屬性變成True時,將使用設置器<Setter Property="Button.Foreground" Value="Red" /> 來將Button的Foreground屬性設置爲Red。這裏有一個隱含的意思是:當Button的IsMouseIOver屬性變成False時,設置器中設置的屬性將回復原值。
你能夠粘貼如下代碼到XamlPad查看效果:
動畫

<Window

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ControlTemplateTest" Height="300" Width="300"

>

<Grid ShowGridLines="True">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="0.2*"/>

<ColumnDefinition Width="0.6*"/>

<ColumnDefinition Width="0.2*"/>

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.4*"/>

</Grid.RowDefinitions>

<Button Content="test btn" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" >

<Button.Template>

<ControlTemplate>

<!--定義視覺樹-->

<Grid>

<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>

<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />

</Grid>

<!--定義視覺樹_end-->

<!--定義觸發器-->

<ControlTemplate.Triggers>

<Trigger Property="Button.IsMouseOver" Value="True">

<Setter Property="Button.Foreground" Value="Red" />

</Trigger>

</ControlTemplate.Triggers>

<!--定義觸發器_End-->

</ControlTemplate>

</Button.Template>

</Button>

</Grid>

</Window>
接下來的一個問題是:若是我要重用個人模板,應該怎麼辦呢?
你須要將模板定義爲資源,其實大多數狀況下,咱們也是這樣作的
參考如下代碼:
ui

<Window.Resources>

<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">

<!--定義視覺樹-->

<Grid>

<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>

<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />

</Grid>

<!--定義視覺樹_end-->

<!--定義觸發器-->

<ControlTemplate.Triggers>

<Trigger Property="Button.IsMouseOver" Value="True">

<Setter Property="Button.Foreground" Value="Red" />

</Trigger>

</ControlTemplate.Triggers>

<!--定義觸發器_End-->

</ControlTemplate>

</Window.Resources>
上面的代碼將咱們原來的模板定義爲窗體範圍內的資源,其中TargetType="Button"指示咱們的模板做用對象爲Button,這樣在整個窗體範圍內的按鈕均可以使用這個模板了,模板的使用方法也很簡單:
spa

<Button Content="test btn" Template="{StaticResource ButtonTemplate}" />
其中的ButtonTemplate是咱們定義的模板的x:Key
你能夠粘貼如下代碼到XamlPad查看效果:
xml

<Window

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ControlTemplateTest" Height="300" Width="300"

>

<Window.Resources>

<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">

<!--定義視覺樹-->

<Grid>

<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>

<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />

</Grid>

<!--定義視覺樹_end-->

<!--定義觸發器-->

<ControlTemplate.Triggers>

<Trigger Property="Button.IsMouseOver" Value="True">

<Setter Property="Button.Foreground" Value="Red" />

</Trigger>

</ControlTemplate.Triggers>

<!--定義觸發器_End-->

</ControlTemplate>

</Window.Resources>

<Grid ShowGridLines="True">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="0.2*"/>

<ColumnDefinition Width="0.6*"/>

<ColumnDefinition Width="0.2*"/>

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.4*"/>

</Grid.RowDefinitions>

<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />

<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

</Grid>

</Window>
額外提一下的是,咱們也能夠在觸發器中,調用一個故事板來達到對事件響應時的動畫效果
參考如下代碼htm

<!--定義動畫資源-->

<ControlTemplate.Resources>

<Storyboard x:Key="MouseClickButtonStoryboard">

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="faceEllipse" Storyboard.TargetProperty="Width" BeginTime="00:00:00">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>

<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="100"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</ControlTemplate.Resources>
咱們爲模板定義了一個動畫資源,此後在模板的觸發器中咱們就能夠調用該資源來實現一個動畫效果了:
對象

<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">

<EventTrigger.Actions>

<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>

</EventTrigger.Actions>

</EventTrigger>
你能夠粘貼如下代碼到XamlPad查看效果:
blog

<Window

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ControlTemplateTest" Height="300" Width="300"

>

<Window.Resources>

<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">

<!--定義視覺樹-->

<Grid>

<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>

<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />

</Grid>

<!--定義視覺樹_end-->

<!--定義動畫資源-->

<ControlTemplate.Resources>

<Storyboard x:Key="MouseClickButtonStoryboard">

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="faceEllipse" Storyboard.TargetProperty="Width" BeginTime="00:00:00">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>

<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="100"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</ControlTemplate.Resources>

<!--定義動畫資源_end-->

<!--定義觸發器-->

<ControlTemplate.Triggers>

<Trigger Property="Button.IsMouseOver" Value="True">

<Setter Property="Button.Foreground" Value="Red" />

</Trigger>

<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">

<EventTrigger.Actions>

<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>

</EventTrigger.Actions>

</EventTrigger>

<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="txtBlock">

<EventTrigger.Actions>

<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>

</EventTrigger.Actions>

</EventTrigger>

</ControlTemplate.Triggers>

<!--定義觸發器_End-->

</ControlTemplate>

</Window.Resources>

<Grid ShowGridLines="True">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="0.2*"/>

<ColumnDefinition Width="0.6*"/>

<ColumnDefinition Width="0.2*"/>

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.3*"/>

<RowDefinition Height="0.4*"/>

</Grid.RowDefinitions>

<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />

<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

</Grid>

</Window>
最好的模板示例:咱們知道每一個控件都有本身默認的模板,這是MS編寫的,若是咱們可以獲得這些模板的XAML代碼,那麼它將是學習模板的最好的示例,
要想得到某個控件ctrl的默認模板,請調用如下方法: