您的位置:首页 > 其它

通过创建 ControlTemplate 自定义现有控件的外观

2016-08-09 10:46 363 查看
ControlTemplate
指定控件的可视结构和可视行为。 可以通过为控件指定新 ControlTemplate 自定义该控件的外观。
创建 ControlTemplate 后,可以在不更改现有控件的功能的情况下更改其外观。
例如,您可以将应用程序中的按钮设置为圆形,而不是默认的方形,但该按钮仍将引发 Click 事件。

本主题介绍 ControlTemplate 的各个部分,演示如何为
Button 创建简单的
ControlTemplate,并说明如何了解控件的控件协定以便可以自定义其外观。
由于您创建 ControlTemplate 时使用的是 XAML,因此无需编写任何代码即可更改控件的外观。
您还可以使用设计器(如 Microsoft Expression Blend)来创建自定义控件模板。
本主题演示 XAML 中自定义 Button 外观的示例,并在本主题的末尾列出了完整示例。
有关使用 Expression Blend 的更多信息,请参见设置支持模板的控件的样式

下图演示使用本主题中创建的 ControlTemplate
Button

一个使用自定义控件模板的按钮



一个使用自定义控件模板并将鼠标指针置于上的按钮



本主题包括下列各节。

系统必备组件
何时应创建 ControlTemplate
更改控件的可视结构
根据控件状态更改控件外观
指定控件在状态间转换时的行为
通过了解控件协定自定义其他控件
完整的示例
相关主题

系统必备组件

本主题假定您理解如何创建和使用控件和样式,如控件中所述。
本主题中讨论的概念适用于从
Control 类继承的元素,但
UserControl 例外。 不能将
ControlTemplate 应用于
UserControl。

何时应创建
ControlTemplate

控件有很多属性(例如 BackgroundForeground
FontFamily),您可以设置这些属性以指定控件外观的不同方面,但通过设置这些属性可进行的更改是有限的。
例如,可以将 CheckBox
Foreground 属性设置为蓝色,将
FontStyle 设置为倾斜。

如果不能为控件创建新的 ControlTemplate,则每个基于 WPF 的应用程序中的所有控件都有相同的一般外观,这限制了创建具有自定义外观的应用程序的能力。
默认情况下,每个 CheckBox 具有相似的特性。
例如,CheckBox 的内容始终位于选择指示符的右侧,复选标记总是用于表示选中了
CheckBox

如果您要自定义通过设置控件的其他属性无法生成的外观,可以创建 ControlTemplate
CheckBox 的示例中,假设您希望复选框的内容位于选择指示符的上方,并希望用 X 表示选中了
CheckBox
可以在 CheckBox
ControlTemplate 中指定这些更改。

下图演示使用默认 ControlTemplate
CheckBox

一个使用默认控件模板的 CheckBox



下图演示使用自定义 ControlTemplate
CheckBox
CheckBox 的内容置于选择指示符的上方并在选中
CheckBox 时显示 X。

一个使用自定义控件模板的 CheckBox



此示例中 CheckBox
ControlTemplate 相对复杂,因此本主题使用较简单的示例为
Button 创建
ControlTemplate

更改控件的可视结构

在 WPF 中,控件通常是复合 FrameworkElement 对象。
当您创建 ControlTemplate 时,组合 FrameworkElement
对象以生成单一控件。 ControlTemplate
必须仅将一个 FrameworkElement 作为其根元素。
根元素通常包含其他 FrameworkElement 对象。
对象组合构成了控件的可视结构。

下面的示例为 Button 创建自定义
ControlTemplate
ControlTemplate
创建 Button 的可视结构。
当您将鼠标指针置于其上或单击它时,该示例不更改按钮的外观。
本主题后面将讨论按钮处于另一种状态时如何更改按钮的外观。

在该示例中,可视结构由下列部分组成:

名为 RootElement 的
Border,作为模板的根
FrameworkElement

一个作为 RootElement 子级的
Grid

一个显示按钮内容的 ContentPresenter
使用 ContentPresenter,可以显示任何类型的对象。

XAML

复制

<ControlTemplate TargetType="Button">
<Border Name="RootElement">

<!--Create the SolidColorBrush for the Background
as an object elemment and give it a name so
it can be referred to elsewhere in the
control template.-->
<Border.Background>
<SolidColorBrush x:Name="BorderBrush" Color="Black"/>
</Border.Background>

<!--Create a border that has a different color
by adding smaller grid. The background of
this grid is specificied by the button's
Background property.-->
<Grid Margin="4" Background="{TemplateBinding Background}">

<!--Use a ContentPresenter to display the Content of
the Button.-->
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4" />
</Grid>

</Border>
</ControlTemplate>


通过使用 TemplateBinding 保留控件属性的功能

创建新的 ControlTemplate 后,您仍可能想要使用公共属性更改控件的外观。
TemplateBinding 标记扩展将
ControlTemplate 中元素的属性绑定到由控件定义的公共属性。
使用
TemplateBinding 时,可让控件上的属性充当模板的参数。 也就是说,在设置控件上的属性时,该值将传递给具有

TemplateBinding 的元素。

下面的示例重复上面的示例中使用
TemplateBinding 标记扩展将 ControlTemplate 中元素的属性绑定到由按钮定义的公共属性部分。

XAML

复制

<Grid Margin="4" Background="{TemplateBinding Background}">

<!--Use a ContentPresenter to display the Content of
the Button.-->
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4" />
</Grid>


在此示例中,Grid 将其
Panel.Background 属性模板绑定到
Control.Background
由于 Panel.Background 是通过模板绑定的,您可以创建使用同一
ControlTemplate 的多个按钮,并将每个按钮的
Control.Background 设置为不同的值。
如果在 ControlTemplate 中未通过模板绑定将
Control.Background 绑定到元素的属性,则设置按钮的
Control.Background 对按钮的外观没有任何影响。

请注意,两个属性的名称不必相同。
在上面的示例中,Button
Control.HorizontalContentAlignment 属性通过模板绑定到
ContentPresenter
FrameworkElement.HorizontalAlignment 属性。
这样就可以水平放置按钮的内容。
ContentPresenter
没有名为 HorizontalContentAlignment 的属性,但 Control.HorizontalContentAlignment
可以绑定到 FrameworkElement.HorizontalAlignment
通过模板绑定属性时,请确保目标属性和源属性是同一类型。

Control
类定义多个必须由控件模板使用的属性,以便可以通过设置这些属性来对控件产生一定的作用。
ControlTemplate
如何使用这些属性视具体的属性而定。 ControlTemplate
必须以下列方式之一使用属性:

ControlTemplate
模板中的某个元素绑定到属性。

ControlTemplate
中的某个元素从父 FrameworkElement 继承属性。

下表列出了 Control 类的控件所继承的可视属性。
它还指示控件的默认控件模板是使用继承的属性值还是必须是通过模板绑定。

Property

使用方法

Background

模板绑定

BorderThickness

模板绑定

BorderBrush

模板绑定

FontFamily

属性继承或模板绑定

FontSize

属性继承或模板绑定

FontStretch

属性继承或模板绑定

FontWeight

属性继承或模板绑定

Foreground

属性继承或模板绑定

HorizontalContentAlignment

模板绑定

Padding

模板绑定

VerticalContentAlignment

模板绑定

此表只列出从 Control 类继承的可视属性。
除了表中列出的属性之外,控件还可以继承父框架元素的 DataContextLanguage
TextDecorations 属性。

另外,如果 ContentPresenter 位于
ContentControl
ControlTemplate 中,则
ContentPresenter 将自动绑定到
ContentTemplate
Content 属性。
同样,位于 ItemsControl
ControlTemplate 中的
ItemsPresenter 将自动绑定到
Items
ItemsPresenter 属性。

下面的示例创建两个使用上一示例中定义的 ControlTemplate 的按钮。
该示例设置每个按钮的 BackgroundForeground
FontSize 属性。
设置 Background 属性也有作用,因为它在
ControlTemplate 中是通过模板绑定的。
即使 Foreground
FontSize 属性不是通过模板绑定的,设置它们也有作用,因为它们的值是通过继承而来的。

XAML

复制

<StackPanel>
<Button Style="{StaticResource newTemplate}"
Background="Navy" Foreground="White" FontSize="14"
Content="Button1"/>

<Button Style="{StaticResource newTemplate}"
Background="Purple" Foreground="White" FontSize="14"
Content="Button2" HorizontalContentAlignment="Left"/>
</StackPanel>


前面示例生成的输出结果与下图相似。

具有不同背景色的两个按钮



根据控件状态更改控件外观

具有默认外观的按钮和上一示例中的按钮之间的区别是默认按钮在处于不同状态时会进行相应变化。
例如,当按下默认按钮或将鼠标指针悬停在默认按钮上方时,按钮外观会产生变化。
虽然 ControlTemplate 不更改控件的功能,但它更改控件的可视行为。
可视行为描述控件处于特定状态时的控件外观。
若要了解控件的功能与可视行为之间的区别,请考虑按钮示例。 按钮的功能是在被单击时引发
Click 事件,而按钮的可视行为是在指向它或按下它时更改按钮的外观。

可以使用 VisualState 对象指定控件在处于特定状态时的外观。
VisualState
包含 Storyboard,用于更改
ControlTemplate 中的元素的外观。
您无需编写任何代码即可实现此目的,这是因为控件的逻辑可通过使用 VisualStateManager 来更改状态。
控件进入 VisualState.Name 属性指定的状态时,Storyboard
开始。 控件退出该状态时,Storyboard 停止。

下面的示例演示当鼠标指针悬停在 Button 上方时更改其外观的
VisualState
Storyboard
通过更改 BorderBrush 的颜色来更改按钮的边框颜色。
如果您查阅本主题开始处的 ControlTemplate 示例,就会记得
BorderBrush 是分配给 Border
Background
SolidColorBrush 的名称。

XAML

复制

<!--Change the border of the button to red when the
mouse is over the button.-->
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Red" />

</Storyboard>
</VisualState>


按照控件协定,该控件负责定义状态,本主题后面的通过了解控件协定自定义其他控件将对此进行详细讨论。
下表列出了为 Button 指定的状态。

VisualState 名称

VisualStateGroup 名称

说明

正常

CommonStates

默认状态。

MouseOver

CommonStates

鼠标指针悬停在控件上。

Pressed(已按下)

CommonStates

控件已按下。

Disabled

CommonStates

控件被禁用。

Focused

FocusStates

控件具有焦点。

Unfocused

FocusStates

控件不具有焦点。

Button
定义两个状态组:CommonStates 组包含 Normal、MouseOver、Pressed 和
Disabled 状态。 FocusStates 组包含
Focused 和 Unfocused 状态。
同一状态组中的状态是互斥的。
控件始终只能处于每组状态中的一种。 例如,Button 甚至在鼠标指针未悬停在其上方时都可以具有焦点,因此处于
Focused 状态的 Button 可以处于
MouseOver、Pressed 或
Normal 状态。

可以将 VisualState 对象添加到
VisualStateGroup 对象中。
可以将 VisualStateGroup 对象添加到
VisualStateManager.VisualStateGroups 附加属性。
下面的示例为 Normal、MouseOver 和
Pressed 等状态定义 VisualState 对象,这些状态全部包含在
CommonStates 组中。
每个 VisualState
Name 与上表中的名称匹配。
省略了 Disabled 状态和
FocusStates 组中的状态以使该示例简短,但本主题结尾的完整示例中包含了这些状态。


注意
请务必设置 ControlTemplate 的根
FrameworkElement 上的
VisualStateManager.VisualStateGroups 附加属性。

XAML

复制

<ControlTemplate TargetType="Button">
<Border Name="RootElement">

<VisualStateManager.VisualStateGroups>

<!--Define the states and transitions for the common states.
The states in the VisualStateGroup are mutually exclusive to
each other.-->
<VisualStateGroup Name="CommonStates">

<!--The Normal state is the state the button is in
when it is not in another state from this VisualStateGroup.-->
<VisualState Name="Normal" />

<!--Change the SolidColorBrush, BorderBrush, to red when the
mouse is over the button.-->
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Red" />
</Storyboard>
</VisualState>

<!--Change the SolidColorBrush, BorderBrush, to Transparent when the
button is pressed.-->
<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Transparent"/>
</Storyboard>
</VisualState>

<!--The Disabled state is omitted for brevity.-->
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<Border.Background>
<SolidColorBrush x:Name="BorderBrush" Color="Black"/>
</Border.Background>

<Grid Background="{TemplateBinding Background}" Margin="4">
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4" />
</Grid>
</Border>
</ControlTemplate>


前面示例生成的输出结果与下图相似。

一个处于正常状态的使用自定义控件模板的按钮



一个处于鼠标悬停状态的使用自定义控件模板的按钮



一个处于按下状态的使用自定义控件模板的按钮



若要查找 WPF 附带的控件的可视状态,请参见Control 样式和模板

指定控件在状态间转换时的行为

在前面的示例中,用户单击按钮时,按钮的外观也会更改,但是用户只有将按钮按下整整 1 秒时才会看到更改效果。
默认情况下,动画要 1 秒后才产生动作。
因为用户可能在不到 1 秒的时间内单击并松开按钮,如果将 ControlTemplate 保持为默认状态,在视觉上将看不到什么变化。

通过将 VisualTransition 对象添加到
ControlTemplate,可以指定动画产生动作的延迟时间,以使控件从一种状态平滑地转换为另一种状态。
创建 VisualTransition 时,指定以下一项或多项内容:

状态转换所耗费的时间。

转换时发生的控件外观的其他更改。

VisualTransition
所应用于的状态。

指定转换的持续时间

可以通过设置 GeneratedDuration 属性来指定转换所需的时间。
上面的示例中有一个 VisualState,用于指定按下按钮时按钮边框变为透明,但如果按钮被快速按下后又松开,该动画将来不及产生动作。
您可以使用 VisualTransition 指定控件转换为按下状态所耗费的时间。
下面的示例指定控件进入按下状态需要百分之一秒的时间。

XAML

复制

<!--Take one hundredth of a second to transition to the
Pressed state.-->
<VisualTransition To="Pressed"
GeneratedDuration="0:0:0.01" />


指定转换期间控件外观的更改

VisualTransition
包含一个当控件转换状态时开始工作的 Storyboard
例如,您可以指定当控件从 MouseOver 状态转换为
Normal 状态时某动画产生动作。
下面的示例创建一个 VisualTransition,它指定当用户将鼠标指针从按钮上移开时,按钮的边框在 1.5 秒内先变为蓝色,然后变为黄色,最后变为黑色。

XAML

复制

<!--Take one and a half seconds to transition from the
MouseOver state to the Normal state.
Have the SolidColorBrush, BorderBrush, fade to blue,
then to yellow, and then to black in that time.-->
<VisualTransition From="MouseOver" To="Normal"
GeneratedDuration="0:0:1.5">
<Storyboard>
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="Color"
Storyboard.TargetName="BorderBrush"
FillBehavior="HoldEnd" >

<ColorAnimationUsingKeyFrames.KeyFrames>

<LinearColorKeyFrame Value="Blue"
KeyTime="0:0:0.5" />
<LinearColorKeyFrame Value="Yellow"
KeyTime="0:0:1" />
<LinearColorKeyFrame Value="Black"
KeyTime="0:0:1.5" />

</ColorAnimationUsingKeyFrames.KeyFrames>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>


指定何时应用 VisualTransition

可以将 VisualTransition 限制为仅应用于某些状态,或在控件进行状态转换的任意时间均可应用。
在上面的示例中,当控件从 MouseOver 状态进入
Normal 状态时应用 VisualTransition;在该示例前面的示例中,当控件进入
Pressed 状态时应用 VisualTransition
通过设置 To
From 属性,限制应用
VisualTransition 的时间。
下表说明从最高限制到最低限制的各个限制级别。

限制类型

过渡开始值

过渡结束值

从一个指定状态到另一个指定状态

VisualState
的名称

VisualState
的名称

从任意状态到指定状态

未设置

VisualState
的名称

从指定状态到任意状态

VisualState
的名称

未设置

从任意状态到任意其他状态

未设置

未设置

您可以具有 VisualStateGroup 中引用相同状态的多个
VisualTransition 对象,但是将按上表中指定的顺序使用它们。
在下面的示例中,有两个 VisualTransition 对象。
当控件从 Pressed 状态转换为
MouseOver 状态时,使用设置了 From
To
VisualTransition
当控件从非 Pressed 的状态转换为
MouseOver 状态时,使用其他状态。

XAML

复制

<!--Take one half second to trasition to the MouseOver state.-->
<VisualTransition To="MouseOver"
GeneratedDuration="0:0:0.5" />

<!--Take one hundredth of a second to transition from the
Pressed state to the MouseOver state.-->
<VisualTransition From="Pressed" To="MouseOver"
GeneratedDuration="0:0:0.01" />


VisualStateGroup
具有 Transitions 属性,该属性包含
VisualTransition 对象,这些对象应用于
VisualStateGroup 中的
VisualState 对象。
作为 ControlTemplate 作者,您可以随意包括任何想要的
VisualTransition
不过,如果 To
From 属性设置为
VisualStateGroup 中不存在的状态名称,将忽略
VisualTransition

下面的示例演示 CommonStates 的
VisualStateGroup
该示例定义每个按钮的下列转换的 VisualTransition

进入 Pressed 状态。

进入 MouseOver 状态。

从 Pressed 状态进入
MouseOver 状态。

从 MouseOver 状态进入
Normal 状态。

XAML

复制

<VisualStateGroup Name="CommonStates">

<!--Define the VisualTransitions that
can be used when the control transitions
between VisualStates that are defined in the
VisualStatGroup.-->
<VisualStateGroup.Transitions>

<!--Take one hundredth of a second to
transition to the Pressed state.-->
<VisualTransition To="Pressed"
GeneratedDuration="0:0:0.01" />

<!--Take one half second to trasition
to the MouseOver state.-->
<VisualTransition To="MouseOver"
GeneratedDuration="0:0:0.5" />

<!--Take one hundredth of a second to transition from the
Pressed state to the MouseOver state.-->
<VisualTransition From="Pressed" To="MouseOver"
GeneratedDuration="0:0:0.01" />

<!--Take one and a half seconds to transition from the
MouseOver state to the Normal state.
Have the SolidColorBrush, BorderBrush, fade to blue,
then to yellow, and then to black in that time.-->
<VisualTransition From="MouseOver" To="Normal"
GeneratedDuration="0:0:1.5">
<Storyboard>
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="Color"
Storyboard.TargetName="BorderBrush"
FillBehavior="HoldEnd" >

<ColorAnimationUsingKeyFrames.KeyFrames>
<LinearColorKeyFrame Value="Blue"
KeyTime="0:0:0.5" />
<LinearColorKeyFrame Value="Yellow"
KeyTime="0:0:1" />
<LinearColorKeyFrame Value="Black"
KeyTime="0:0:1.5" />

</ColorAnimationUsingKeyFrames.KeyFrames>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>

<!--The remainder of the VisualStateGroup is the
same as the previous example.-->

<VisualState Name="Normal" />

<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Red" />

</Storyboard>
</VisualState>

<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Transparent"/>
</Storyboard>
</VisualState>

<!--The Disabled state is omitted for brevity.-->

</VisualStateGroup>


通过了解控件协定自定义其他控件

使用 ControlTemplate 指定其可视结构(通过使用
FrameworkElement 对象)和可视行为(通过使用
VisualState 对象)的控件使用了部件控件模型。
WPF 4 附带的许多控件都使用此模型。
ControlTemplate
作者需要了解的部件是通过控件协定传递的。 了解控件协定的组成部分之后,您可以自定义使用部件控件模型的任何控件的外观。

控件协定具有三个元素:

控件的逻辑使用的可视元素。

控件的状态以及每个状态所属的组。

以可视方式影响控件的公共属性。

控件协定中的可视元素

有时,控件的逻辑与 ControlTemplate 中的
FrameworkElement 进行交互。
例如,控件可能处理其元素之一的一个事件。
如果控件期望在 ControlTemplate 中找到特定
FrameworkElement,它必须将该信息传递给
ControlTemplate 作者。
控件使用 TemplatePartAttribute 传递期望使用的元素类型,以及元素应具有的名称。
Button
在其控件协定中没有 FrameworkElement 部件,但是其他控件(例如
ComboBox)在其控件协定中有该部件。

下面的示例演示 ComboBox 类上指定的
TemplatePartAttribute 对象。
ComboBox
的逻辑期望在其 ControlTemplate 中找到名为
PART_EditableTextBox 的 TextBox 和名为
PART_Popup 的 Popup

C#
VB

复制

[TemplatePartAttribute(Name = "PART_EditableTextBox", Type = typeof(TextBox))]
[TemplatePartAttribute(Name = "PART_Popup", Type = typeof(Popup))]
public class ComboBox : ItemsControl
{
}


下面的示例演示一个简单的 ControlTemplate,该对象用于
ComboBox,其中包括由
ComboBox 类上的
TemplatePartAttribute 对象指定的元素。

XAML

复制

<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton x:Name="DropDownToggle"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="-1" HorizontalContentAlignment="Right"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}">
<Path x:Name="BtnArrow" Height="4" Width="8"
Stretch="Uniform" Margin="0,0,6,0"  Fill="Black"
Data="F1 M 300,-190L 310,-190L 305,-183L 301,-190 Z " />
</ToggleButton>
<ContentPresenter x:Name="ContentPresenter" Margin="6,2,25,2"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}">
</ContentPresenter>
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Focusable="False"
Background="{TemplateBinding Background}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,3,23,3"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>

<Popup x:Name="PART_Popup"
IsOpen="{TemplateBinding IsDropDownOpen}">
<Border x:Name="PopupBorder"
HorizontalAlignment="Stretch" Height="Auto"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Black" Background="White" CornerRadius="3">
<ScrollViewer x:Name="ScrollViewer" BorderThickness="0" Padding="1">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</Popup>

</Grid>
</ControlTemplate>


控件协定中的状态

控件状态也是控件协定的组成部分。
Button 创建
ControlTemplate 的示例演示了如何根据
Button 的状态指定其外观。
可以为每个指定的状态创建一个 VisualState,并将所有共享
GroupName
VisualState 对象放在一个
VisualStateGroup 中,如本主题前面的根据控件状态更改控件外观中所述。
第三方控件应使用 TemplateVisualStateAttribute 指定状态,这样可使设计器工具(如 Expression
Blend)为创作控件模板来公开控件的状态。

若要查找 WPF 附带的控件的控件协定,请参见Control 样式和模板

控件协定中的属性

控件协定中还包含了以可视方式影响控件的公共属性。
可以设置这些属性来更改控件的外观,无需创建新的 ControlTemplate
您还可以使用
TemplateBinding 标记扩展将 ControlTemplate 中元素的属性绑定到由
Button 定义的公共属性。

下面的示例演示按钮的控件协定。

C#
VB

复制

[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
[TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Pressed", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Unfocused", GroupName = "FocusStates")]
[TemplateVisualState(Name = "Focused", GroupName = "FocusStates")]
public class Button : ButtonBase
{
public static readonly DependencyProperty BackgroundProperty;
public static readonly DependencyProperty BorderBrushProperty;
public static readonly DependencyProperty BorderThicknessProperty;
public static readonly DependencyProperty ContentProperty;
public static readonly DependencyProperty ContentTemplateProperty;
public static readonly DependencyProperty FontFamilyProperty;
public static readonly DependencyProperty FontSizeProperty;
public static readonly DependencyProperty FontStretchProperty;
public static readonly DependencyProperty FontStyleProperty;
public static readonly DependencyProperty FontWeightProperty;
public static readonly DependencyProperty ForegroundProperty;
public static readonly DependencyProperty HorizontalContentAlignmentProperty;
public static readonly DependencyProperty PaddingProperty;
public static readonly DependencyProperty TextAlignmentProperty;
public static readonly DependencyProperty TextDecorationsProperty;
public static readonly DependencyProperty TextWrappingProperty;
public static readonly DependencyProperty VerticalContentAlignmentProperty;

public Brush Background { get; set; }
public Brush BorderBrush { get; set; }
public Thickness BorderThickness { get; set; }
public object Content { get; set; }
public DataTemplate ContentTemplate { get; set; }
public FontFamily FontFamily { get; set; }
public double FontSize { get; set; }
public FontStretch FontStretch { get; set; }
public FontStyle FontStyle { get; set; }
public FontWeight FontWeight { get; set; }
public Brush Foreground { get; set; }
public HorizontalAlignment HorizontalContentAlignment { get; set; }
public Thickness Padding { get; set; }
public TextAlignment TextAlignment { get; set; }
public TextDecorationCollection TextDecorations { get; set; }
public TextWrapping TextWrapping { get; set; }
public VerticalAlignment VerticalContentAlignment { get; set; }
}


创建 ControlTemplate 时,最方便的方法通常是打开现有
ControlTemplate,然后对其进行更改。
可以执行下列操作之一来更改现有 ControlTemplate

使用设计器(例如 Expression Blend),该设计器提供用于创建控件模板的图形用户界面。
有关更多信息,请参见设置支持模板的控件的样式

获取默认的 ControlTemplate 并对其进行编辑。
若要查找 WPF 附带的默认控件模板,请参见Default WPF Themes(默认 WPF 主题)。

完整的示例

下面的示例演示本主题讨论的完整 ButtonControlTemplate

XAML

复制

<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button" x:Key="newTemplate">
<!--Set the Background, Foreground, FontSize, Width,
Height, Margin, and Template properties for
the Button.-->
<Setter Property="Background" Value="Navy"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="RootElement">
<VisualStateManager.VisualStateGroups>

<!--Define the states and transitions for the common states.
The states in the VisualStateGroup are mutually exclusive to
each other.-->
<VisualStateGroup Name="CommonStates">

<!--Define the VisualTransitions that can be used when the control
transitions between VisualStates that are defined in the
VisualStatGroup.-->
<VisualStateGroup.Transitions>

<!--Take one hundredth of a second to transition to the Pressed state.--> <VisualTransition To="Pressed" GeneratedDuration="0:0:0.01" /><!--Take one half second to trasition to the MouseOver state.--> <VisualTransition To="MouseOver" GeneratedDuration="0:0:0.5" /> <!--Take one hundredth of a second to transition from the Pressed state to the MouseOver state.--> <VisualTransition From="Pressed" To="MouseOver" GeneratedDuration="0:0:0.01" /><!--Take one and a half seconds to transition from the
MouseOver state to the Normal state.
Have the SolidColorBrush, BorderBrush, fade to blue,
then to yellow, and then to black in that time.-->
<VisualTransition From="MouseOver" To="Normal"
GeneratedDuration="0:0:1.5">
<Storyboard>
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="Color"
Storyboard.TargetName="BorderBrush"
FillBehavior="HoldEnd" >

<ColorAnimationUsingKeyFrames.KeyFrames>

<LinearColorKeyFrame Value="Blue"
KeyTime="0:0:0.5" />
<LinearColorKeyFrame Value="Yellow"
KeyTime="0:0:1" />
<LinearColorKeyFrame Value="Black"
KeyTime="0:0:1.5" />

</ColorAnimationUsingKeyFrames.KeyFrames>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>

<!--The Normal state is the state the button is in
when it is not in another state from this VisualStateGroup.
There is no special visual behavior for this state, but
the VisualState must be defined in order for the button
to return to its initial state.-->
<VisualState x:Name="Normal" />

<!--Change the border of the button to red when the mouse is over the button.--> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Storyboard.TargetName="BorderBrush" Storyboard.TargetProperty="Color" To="Red" /> </Storyboard> </VisualState><!--Change the border of the button to Transparent when the
button is pressed.-->
<VisualState x:Name="Pressed">
<Storyboard >
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Transparent"
/>
</Storyboard>
</VisualState>

<!--Show the DisabledRect when the IsEnabled property on
the button is false.-->
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="DisabledRect"
Storyboard.TargetProperty="Opacity"
To="1" Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>

<!--Define the states and transitions for the focus states.
The states in the VisualStateGroup are mutually exclusive to
each other.-->
<VisualStateGroup x:Name="FocusStates">

<!--Define the VisualStates in this VistualStateGroup.-->
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="FocusVisual"
Storyboard.TargetProperty="Visibility" Duration
="0">

<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName="FocusVisual"
Storyboard.TargetProperty="Visibility"
Duration="0">

<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<!--Create the SolidColorBrush for the Background
as an object elemment and give it a name so
it can be referred to elsewhere in the control template.-->
<Border.Background>
<SolidColorBrush x:Name="BorderBrush" Color="Black"/>
</Border.Background>

<!--Create a border that has a different color by adding smaller grid.
The background of this grid is specified by the button's Background
property.-->
<Grid Background="{TemplateBinding Background}" Margin="4">

<!--Create a Rectangle that indicates that the
Button has focus.-->
<Rectangle Name="FocusVisual"
Visibility="Collapsed" Margin="2"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="1"
StrokeDashArray="1.5 1.5"/>

<!--Use a ContentPresenter to display the Content of
the Button.-->
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="4,5,4,4" />

<!--Create a rectangle that causes the button to appear
grayed out when it is disabled.-->
<Rectangle x:Name="DisabledRect"
Fill="#A5FFFFFF"
Opacity="0" IsHitTestVisible="false" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

</StackPanel.Resources>

<Button Style="{StaticResource newTemplate}"
Content="Button1"/>

<Button Style="{StaticResource newTemplate}"
Background="Purple"
Content="Button2" />
</StackPanel>



请参见

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: