在本文中
在 XAML 框架中,如果要自定义控件的可视结构和可视行为,请创建控件模板。控件有多个属性,如 Background、Foreground 以及FontFamily,可以设置这些属性以指定控件外观的多个方面。但是可以通过设置这些属性所做的更改有限。可以使用 ControlTemplate 类创建提供其他自定义的模板。在此处,我们介绍如何创建 ControlTemplate 以自定义 CheckBox 控件的外观。
路线图: 本主题与其他主题有何关联?请参阅:
自定义控件模板示例
在默认情况下,CheckBox 控件将其内容(字符串或 CheckBox 旁的对象)放在选择框的右侧。这是 CheckBox 的可视结构。在默认情况下,复选标记表示用户已选定 CheckBox。这是 CheckBox 的可视行为。你可以通过为 CheckBox 创建 ControlTemplate 来更改这些特性。例如,假定你想要让复选框的内容显示在选择框下方,并且你想要用 X 来表示用户已选定复选框。你可以在 CheckBox 的 ControlTemplate 中指定这些特性。
下面是分别在 Unchecked
、Checked
和 Indeterminate
状态下使用默认 ControlTemplate 的 CheckBox。
要为控件使用自定义模板,请将 ControlTemplate 分配给控件的 Template 属性。下面是使用称为CheckBoxTemplate1
的 ControlTemplate 的 CheckBox。我们在下一节介绍 ControlTemplate 的 Extensible Application Markup Language (XAML)。
<CheckBox Content="CheckBox" Template="{StaticResource CheckBoxTemplate1}" IsThreeState="True" Margin="20"/>
下面是在应用模板后,CheckBox 在 Unchecked
、Checked
和 Indeterminate
状态下的外观。
指定控件的可视结构。
当你创建 ControlTemplate 时,要结合 FrameworkElement 对象来构建一个单一的控件。ControlTemplate只能有一个 FrameworkElement 作为其根元素。该根元素通常包含其他 FrameworkElement 对象。这些对象的组合组成控件的可视结构。
下面的 XAML 为 CheckBox 创建了 ControlTemplate,指定控件的内容显示在选择框的下方。根元素为Border。该示例指定 Path 来创建 X,表示用户已选定 CheckBox,并用 Ellipse 表示不确定状态。请注意,Opacity 在 PathEllipse 上设置为 0,因此在默认情况下,两者都不会显示。
<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="{ThemeResource CheckBoxBackgroundThemeBrush}" Stroke="{ThemeResource CheckBoxBorderThemeBrush}" StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}" UseLayoutRounding="False" Height="21" Width="21"/> <!-- 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"/> <Rectangle x:Name="IndeterminateGlyph" Fill="{ThemeResource CheckBoxForegroundThemeBrush}" Height="9" Width="9" Opacity="0" UseLayoutRounding="False" /> <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}" Grid.Row="1" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> </Border> </ControlTemplate>
指定控件的可视行为
可视行为指定控件在确定状态下的外观。CheckBox 控件具有 3 中复选状态:Checked
、Unchecked
和Indeterminate
。IsChecked 属性的值确定 CheckBox 的状态,其状态确定方框中显示的符号。
下表列出了 IsChecked 的可能值,CheckBox 的响应状态,以及 CheckBox 的外观。
IsChecked 值 | CheckBox 状态 | CheckBox 外观 |
true | Checked |
包含 "X"。 |
false | Unchecked |
空白。 |
null | Indeterminate |
包含一个矩形。 |
使用 VisualState 对象可指定控件在确定状态下的外观。VisualState 包含可更改 ControlTemplate 中元素外观的 Storyboard。当控件切换到 VisualState.Name 属性指定的状态时,Storyboard 就会开始。当控件退出该状态时,Storyboard 就会停止。你可以将 VisualState 对象添加到 VisualStateGroup 对象。还可以将VisualStateGroup 对象添加到 VisualStateManager.VisualStateGroups 附加的属性,这些对象在ControlTemplate 的根 FrameworkElement 上设置。
以下 XAML 介绍在 Checked
、Unchecked
和 Indeterminate
状态下的 VisualState 对象。该示例在 Border 上设置 VisualStateManager.VisualStateGroups 附加属性,它是 ControlTemplate 的根元素。Checked
VisualState 指定名为 CheckGlyph
的 Path(已在前面的示例中介绍)的 Opacity 为 1。Indeterminate
VisualState 指定名为 IndeterminateGlyph
的 Ellipse 的 Opacity 为 1。Unchecked
VisualState 没有 Storyboard,因此 CheckBox 恢复为默认外观。
<ControlTemplate TargetType="CheckBox" x:Key="CheckBoxTemplate1"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CheckStates"> <VisualState x:Name="Checked"> <Storyboard> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="CheckGlyph"/> </Storyboard> </VisualState> <VisualState x:Name="Unchecked"/> <VisualState x:Name="Indeterminate"> <Storyboard> <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="IndeterminateGlyph"/> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="25"/> </Grid.RowDefinitions> <Rectangle x:Name="NormalRectangle" Fill="{ThemeResource CheckBoxBackgroundThemeBrush}" Stroke="{ThemeResource CheckBoxBorderThemeBrush}" StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}" UseLayoutRounding="False" Height="21" Width="21"/> <!-- 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"/> <Rectangle x:Name="IndeterminateGlyph" Fill="{ThemeResource CheckBoxForegroundThemeBrush}" Height="9" Width="9" Opacity="0" UseLayoutRounding="False" /> <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}" Grid.Row="1" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> </Border> </ControlTemplate>
为了更深入地理解 VisualState 对象的工作机制,请思考当 CheckBox 从 Unchecked
状态切换到 Checked
状态,然后切换到 Indeterminate
状态,然后又恢复为 Unchecked
状态时,会发生什么。下表介绍了这些转换。
状态转换 | 引发的结果 | 转换完成时的 CheckBox 外观 |
从 Unchecked 到 Checked 。 |
Checked VisualState 的 Storyboard 开始,所以 CheckGlyph 的 Opacity为 1。 |
显示 X。 |
从 Checked 到Indeterminate 。 |
Indeterminate VisualState 的 Storyboard 开始,所以IndeterminateGlyph 的 Opacity 为 1。 Checked VisualState 的Storyboard 结束,所以 CheckGlyph 的 Opacity 为 0。 |
显示一个圆形。 |
从Indeterminate 到 Unchecked 。 |
Indeterminate VisualState 的 Storyboard 结束,所以IndeterminateGlyph 的 Opacity 为 0。 |
不显示任何符号。 |
有关如何创建控件视觉状态和(特别是)如何使用 Storyboard 类和动画类型的详细信息,请参阅视觉状态的情节提要动画。
使用工具轻松处理主题
将主题应用到控件的一种快捷方式是,在 Microsoft Visual Studio XAML 设计界面上,右键单击控件并选择“编辑主题”或“编辑样式”(取决于右键单击的控件)。然后,通过选择“应用资源”来应用现有主题,或通过选择“创建空项”来定义一个新主题。
控件和辅助功能
为控件创建新模板时,除了可能会更改控件的行为和视觉外观外,还可能会更改控件自行代表辅助功能框架的方式。Windows 运行时支持 Microsoft UI 自动化框架用于辅助功能。所有默认控件及其模板都支持适用于控件的用途和功能的常见 UI 自动化控件类型和模式。这些控件类型和模式由 UI 自动化客户端(如辅助技术)进行解释,这样允许控件作为较大辅助应用 UI 的一部分进行访问。
若要分离基本控件逻辑以及符合 UI 自动化的某些体系结构要求,控件类在独立类(自动化对等)中包含辅助功能支持。有时自动化对等会与控件模板有交互,因为对等预期某些命名部件存在于模板中,以便可能会使用诸如允许辅助技术调用按钮操作的功能。
创建全新的自定义控件时,有时还希望随之一起新建自动化对等。有关详细信息,请参阅自定义的自动化对等。
了解有关控件默认模板的详细信息
如果你查看添加控件和内容下的各种主题,会发现一些记录了现有 Windows 运行时控件的默认控件模板的主题。例如,存在名为 AppBar 样式和模板的主题,它是添加应用栏下的子主题。
记录了 Windows 运行时控件样式和模板的主题向你显示的起始 XAML 摘录与使用之前描述的编辑主题或编辑样式技术时看到的相同。每个主题都将列出视觉状态的名称、使用的主题资源,以及包含该模板的样式的完整 XAML。如果你已开始修改模板并要查看原始模板的外观,或者想要验证你的新模板是否具有所有所需的命名视觉状态,这些主题将是非常有用的指南。
控件模板中的主题资源
对于 XAML 模板中的某些属性,你可能已注意到使用 ThemeResource 标记扩展的资源引用。这是一种可使单个控件模板使用资源的技术,这些资源可能采用不同的值,具体取决于当前哪个主题处于活动状态。这对于画笔和颜色尤其重要,因为主题的主要目的是使用户选择应用于整个系统的是深色主题、浅色主题,还是高对比度主题。使用 XAML 资源系统的应用可以使用适合该主题的资源集,以便应用 UI 中的主题选择可以反映用户的整个系统的主题选择。
添加控件和内容下还特别提供了其他主题,以记录现有 Windows 运行时控件的默认控件模板。作为此内容的一部分,这些主题还列出了默认模板使用的主题资源(主要是画笔)以及它们在每个主题下的值。同样,完整的主题资源集记录在 XAML 主题资源引用中。