Style 只能就 element 已有的 property 設定,template 可以完全取代
element 的視覺外觀 (visual tree),而不會改變原有的功能與程式碼。Control templates 都是由 ControlTemplate
class 所衍生的。
Control Templates
利用 ControlTemplate 定義 control 的外觀,並以 x:Key 設定 template 的名稱。
<Grid>
<Grid.Resources>
<ControlTemplate x:Key="buttonTemplate">
<Grid Width="100" Height="30">
<Rectangle Fill="Bisque" Stroke="Brown" RadiusX="5" RadiusY="5"/>
<Ellipse Width="24" Height="24" Fill="Brown" HorizontalAlignment="Left" Margin="3"/>
</Grid>
</ControlTemplate>
</Grid.Resources>
<Button Template="{StaticResource buttonTemplate}"/>
</Grid>
|
Control Template Trigger
如同 style,template 也可以利用 trigger 來動態調整 control 的外觀。
<Grid>
<Grid.Resources>
<ControlTemplate x:Key="buttonTemplate">
<Grid Width="100" Height="30">
<Rectangle Fill="Bisque" Stroke="Brown" RadiusX="5" RadiusY="5"/>
<Ellipse x:Name="Icon" Width="24" Height="24" Fill="Brown" HorizontalAlignment="Left" Margin="3"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter TargetName="Icon" Property="Fill" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Grid.Resources>
<Button Template="{StaticResource buttonTemplate}">OK</Button>
</Grid>
|
Restricting the Target Type
與 style 相同,template 也可以指定要套用的類別,但在 dictionary 中,不允許移除 x:Key 的設定。
<ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
<Grid Width="100" Height="30">
…
</Grid>
</ControlTemplate>
|
Content Property
在前面的範例中,我們希望能顯示出原有 Button.Content 中的文字, 則可以透過 TemplateBinding 來達成。
<TextBlock Text="{TemplateBinding Content}" Margin="36,0,0,0" VerticalAlignment="Center"/>
|
不過要特別注意,Button 的 Content 不一定是文字型態,因此使用 ContentControl 或 ContentPresenter 是比較正確的寫法。ContentPresenter
比 ContentControl 輕些,因為 ControlControl 在 ContentPresenter 外面又包了一層物件。
<ContentPresenter Content="{TemplateBinding Content}"/>
|
Other Properties
除了 content property,也可以沿用原來物件的其他 property,例如 Background, FontSize,..等…。下列的範例,要特別注意,矩形的背景,直接使用 TemplateBinding 來達成,而圓形使用漸層上色,必須將 brush
轉換成 color,因此語法不同:
<ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
<Grid Width="100" Height="30">
<Rectangle Fill="{TemplateBinding Background}" Stroke="Brown" RadiusX="5" RadiusY="5"/>
<Ellipse x:Name="Icon" Width="24" Height="24" HorizontalAlignment="Left" Margin="3">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0, 1">
<GradientStop Offset="0"
Color="{Binding RelativeSource={RelativeSource
TemplatedParent}, Path=Foreground.Color}"/>
<GradientStop Offset="1" Color="Red"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Content="{TemplateBinding Content}" Margin="36,0,0,0" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
|
在上面的範例中,使用 Binding 取代 TemplateBinding,可以獲得更細密的內容。另外,在使用
trigger 時,因為是在 visual tree 外面動態處理,只能使用 Binding 而無法使用 TemplateBinding。
Control Parts
許多較複雜的 control 是由不同的 part 所組成,例如 ComboBox 由 PART_EditableTextBox 與 PART_Popup 組成。WPF 內建的 control 皆以 PART_xxx 命名。當設計 template 時,可能需要注意到這些細節。
Control States
在 WPF 4.0 之後,增加了 State 的設計,可以透過 VisualStateManager.GoToState 切換狀態。一個 control 可以有多個 state group,每個 group 又有多個 state。例如 Button 的
CommonStates group 包含了 Normal, MouseOver, Pressed, Disabled 四種狀態,而 FocusStates group 包含了 Unfocused,
Focused 兩種狀態。在任何時候,Button 一定會在每一組 group 中的某個 state 中,例如
MouseOver & Focused。
設計 template 時,可以使用
<VisualStateManager.VirtualStateGroup> 來設計各種狀態的轉換方式。
Mixing Templates With Styles
將 template 設置在 style 中,可以用來取代 WPF 預設的 style,使用方式如下。注意在下列的範例中,ControlTemplate 可以不用 x:Key 來指定 template 的名稱。
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
…
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Button>OK</Button>
</Grid>
|
沒有留言:
張貼留言