可以通过在 XAML 框架中创建控件模板来自定义控件的视觉结构和视觉行为。 控件具有许多属性,如 Background、 Foreground 和 FontFamily,你可以将其设置为指定控件外观的不同方面。 但是,通过设置这些属性可以做出的更改受到限制。 可以使用 ControlTemplate 类创建模板来指定其他自定义项。 下面介绍如何创建 ControlTemplate 以自定义 CheckBox 控件的外观。
重要 API: ControlTemplate 类、 Control.Template 属性
自定义控件模板示例
默认情况下, CheckBox 控件将其内容( CheckBox 旁边的字符串或对象)放在选定框右侧,复选标记指示用户选择了 CheckBox。 这些特征表示 CheckBox 的可视结构和视觉行为。
下面是一个使用默认 ControlTemplate 的 CheckBox,显示在 Unchecked
、Checked
和 Indeterminate
状态中。
可以通过为 CheckBox 创建 ControlTemplate 来更改这些特征。 例如,如果希望复选框的内容位于选择框下方,并且希望使用 X 指示用户选择了复选框。 在 CheckBox 的 ControlTemplate 中指定这些特征。
若要对控件使用自定义模板,请将 ControlTemplate 分配给控件的 Template 属性。 下面是使用名为 的 ControlTemplate 的 CheckBoxTemplate1
。 我们将在下一部分显示 ControlTemplate 的可扩展应用程序标记语言(XAML)。
<CheckBox Content="CheckBox" Template="{StaticResource CheckBoxTemplate1}" IsThreeState="True" Margin="20"/>
应用模板后,以下是 CheckBox 在 Unchecked
、Checked
和 Indeterminate
状态下的外观。
指定控件的可视结构
创建 ControlTemplate 时,将 FrameworkElement 对象合并为生成单个控件。 ControlTemplate 必须只有一个 FrameworkElement 作为其根元素。 根元素通常包含其他 FrameworkElement 对象。 对象的组合构成控件的视觉结构。
此 XAML 为 CheckBox 创建 ControlTemplate,指定控件的内容位于选择框下方。 根元素是 边框。 该示例指定了一个 路径,用于创建一个 X,表示用户选择了 复选框,以及一个 椭圆,表示不确定状态。 请注意, Opacity 在 Path 和 Ellipse 上设置为 0,这样默认情况下,两者都不会出现。
TemplateBinding 是一种特殊绑定,可将控件模板中属性的值链接到模板化控件上其他公开属性的值。 TemplateBinding 只能在 XAML 中的 ControlTemplate 定义中使用。 有关详细信息 ,请参阅 TemplateBinding 标记扩展 。
注释
从 Windows 10 版本 1809(SDK 17763)开始,你可以在使用 TemplateBinding的位置使用 x:Bind 标记扩展。 有关详细信息 ,请参阅 TemplateBinding 标记扩展 。
<ControlTemplate x:Key="CheckBoxTemplate1" TargetType="CheckBox">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Rectangle x:Name="NormalRectangle" Fill="Transparent" Height="20" Width="20"
Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}"
UseLayoutRounding="False"/>
<!-- Create an X to indicate that the CheckBox is selected. -->
<Path x:Name="CheckGlyph"
Data="M103,240 L111,240 119,248 127,240 135,240 123,252 135,264 127,264 119,257 111,264 103,264 114,252 z"
Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
FlowDirection="LeftToRight"
Height="14" Width="16" Opacity="0" Stretch="Fill"/>
<Ellipse x:Name="IndeterminateGlyph"
Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
Height="8" Width="8" Opacity="0" UseLayoutRounding="False" />
<ContentPresenter x:Name="ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}" Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
</ControlTemplate>
指定控件的视觉行为
视觉行为指定控件处于特定状态时的外观。
CheckBox 控件有 3 个检查状态:Checked
、Unchecked
和Indeterminate
。
IsChecked 属性的值确定 CheckBox 的状态,其状态确定框中显示的内容。
下表列出了 IsChecked 的可能值、 CheckBox 的相应状态以及 CheckBox 的外观。
IsChecked 值 | CheckBox 的状态 | CheckBox 外观 |
---|---|---|
true | Checked |
包含“X”。 |
假 | Unchecked |
空白。 |
零 | Indeterminate |
包含一个圆。 |
使用 VisualState 对象指定控件处于特定状态时的外观。 VisualState 包含 Setter 或 Storyboard,用于更改 ControlTemplate元素的外观。 当控件进入 VisualState.Name 属性指定的状态时,将应用 Setter 或 Storyboard 中的属性。 当控件退出状态时,将删除更改。 将 VisualState 对象添加到 VisualStateGroup 对象。 将 VisualStateGroup 对象添加到 VisualStateManager.VisualStateGroups 附加属性,该属性在 ControlTemplate的根 FrameworkElement 上设置。
此 XAML 显示 、和 Checked
状态的 Indeterminate
对象。 该示例在 Border上设置了 VisualStateManager.VisualStateGroups 附加属性,该属性是 ControlTemplate的根元素。
Checked
VisualState 指定名为 的 路径CheckGlyph
(上一示例中所示)为 1。
Indeterminate
VisualState 指定名为 的 椭圆 的 IndeterminateGlyph
为 1。
Unchecked
VisualState 没有 Setter 或 Storyboard,因此 CheckBox 返回其默认外观。
<ControlTemplate x:Key="CheckBoxTemplate1" TargetType="CheckBox">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Target="CheckGlyph.Opacity" Value="1"/>
</VisualState.Setters>
<!-- This Storyboard is equivalent to the Setter. -->
<!--<Storyboard>
<DoubleAnimation Duration="0" To="1"
Storyboard.TargetName="CheckGlyph" Storyboard.TargetProperty="Opacity"/>
</Storyboard>-->
</VisualState>
<VisualState x:Name="Unchecked"/>
<VisualState x:Name="Indeterminate">
<VisualState.Setters>
<Setter Target="IndeterminateGlyph.Opacity" Value="1"/>
</VisualState.Setters>
<!-- This Storyboard is equivalent to the Setter. -->
<!--<Storyboard>
<DoubleAnimation Duration="0" To="1"
Storyboard.TargetName="IndeterminateGlyph" Storyboard.TargetProperty="Opacity"/>
</Storyboard>-->
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Rectangle x:Name="NormalRectangle" Fill="Transparent" Height="20" Width="20"
Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}"
UseLayoutRounding="False"/>
<!-- Create an X to indicate that the CheckBox is selected. -->
<Path x:Name="CheckGlyph"
Data="M103,240 L111,240 119,248 127,240 135,240 123,252 135,264 127,264 119,257 111,264 103,264 114,252 z"
Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
FlowDirection="LeftToRight"
Height="14" Width="16" Opacity="0" Stretch="Fill"/>
<Ellipse x:Name="IndeterminateGlyph"
Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
Height="8" Width="8" Opacity="0" UseLayoutRounding="False" />
<ContentPresenter x:Name="ContentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}" Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
</ControlTemplate>
若要更好地了解 VisualState 对象的工作原理,请考虑 CheckBox 从 Unchecked
状态到 Checked
状态、 Indeterminate
状态,然后返回到 Unchecked
状态时会发生什么情况。 下面是过渡。
状态转换 | 发生的情况 | 切换完成后的 CheckBox 外观 |
---|---|---|
从 Unchecked 到 Checked 。 |
Setter 的值应用于 Checked VisualState,因此 的 CheckGlyph 为 1。 |
将显示 X。 |
从 Checked 到 Indeterminate 。 |
Setter 的值应用于 Indeterminate VisualState,因此 的 IndeterminateGlyph 为 1。
VisualState 的 Checked Setter 值被移除,因此 CheckGlyph 不透明度 为 0。 |
将显示一个圆。 |
从 Indeterminate 到 Unchecked 。 |
Indeterminate
VisualState 的 Setter 值已被删除,因此 IndeterminateGlyph 的 不透明度 为 0。 |
不显示任何内容。 |
有关如何为控件创建视觉状态的详细信息,特别是如何使用 Storyboard 类和动画类型,请参阅 视觉状态的情节提要动画。
使用工具轻松处理主题
将主题应用于控件的快速方法是右键单击 Visual Studio 文档大纲 Microsoft上的控件,然后选择 “编辑主题” 或 “编辑样式”(具体取决于您右键单击的控件)。 然后,可以通过选择 “应用资源 ”来应用现有主题,也可以通过选择“ 创建空”来定义一个新主题。
控件和辅助功能
为控件创建新模板时,除了可能更改控件的行为和视觉外观外,还可以更改控件将自身表示为辅助功能框架的方式。 Windows 应用支持 Microsoft UI 自动化框架以实现辅助功能。 所有默认控件及其模板都支持适用于控件用途和函数的常见 UI 自动化控件类型和模式。 这些控件类型和模式由 UI 自动化客户端(如辅助技术)解释,这使控件可以作为较大辅助应用 UI 的一部分进行访问。
为了分离基本控制逻辑并满足 UI 自动化的某些架构要求,控件类将其辅助功能支持包含在一个单独的类中,该类称为自动化对等类。 自动化对等有时与控件模板交互,因为对等方希望模板中存在某些命名部件,因此可以启用辅助技术来调用按钮操作等功能。
创建全新的自定义控件时,有时还希望创建新的自动化同级对象与之匹配。 有关详细信息,请参阅 自定义自动化伙伴。
详细了解控件的默认模板
说明 XAML 控件的样式和模板的主题会展示出相同的起始 XAML 摘录,你可以看到这些摘录就像使用了前面介绍的 编辑主题 或 编辑样式 技术一样。 每个主题列出了视觉状态的名称、使用的主题资源以及包含模板的样式的完整 XAML。 如果已开始修改模板并想要查看原始模板的外观,或者验证新模板是否具有所有必需的命名视觉状态,这些主题可能很有用。
控件模板中的主题资源
对于 XAML 示例中的某些属性,你可能已注意到使用 {ThemeResource} 标记扩展的资源引用。 这是一种技术,使单个控件模板能够使用资源,这些资源可以是不同的值,具体取决于当前处于活动状态的主题。 对于画笔和颜色来说,这尤其重要,因为主题的主要目的是让用户选择是想要应用于系统整体的深色、浅色还是高对比度主题。 使用 XAML 资源系统的应用可以使用适用于该主题的资源集,以便应用的 UI 中的主题选项反映用户的全系统主题选择。