XAML 样式
可以使用 XAML 框架通过多种方式自定义应用的外观。 通过样式可以设置控件属性,并重复使用这些设置,以便保持多个控件具有一致的外观。
WinUI 和样式
从 WinUI 2.2 开始,我们使用 WinUI 为 UI 组件提供新的视觉样式更新。 如果你发现 UI 未更新到最新样式,请务必更新到最新的 WinUI NuGet 包。
从 WinUI 2.6 开始,我们为大多数控件提供新样式,并提供一个新的版本控制系统,让你可以根据需要恢复到以前的控件样式。 建议你使用新样式,因为它们更符合 Windows 的设计方向。 但是,如果你的方案不支持新样式,以前的版本仍然可用。
使用 WinUI 版本 2 时,可以通过设置包含在 Application.Resources
中的 XamlControlsResources
上的 ControlsResourcesVersion
属性来更改样式版本。 ControlsResourcesVersion
默认为枚举值 Version2
。
将此值设置为 Version1
会导致 XamlControlsResources
加载以前的样式版本,而不是最新 WinUI 版本使用的新样式。 不支持在运行时更改此属性,并且 Visual Studio 的热重载功能将不起作用;但是,重新生成应用程序后,你会看到控件样式发生了变化。
<Application.Resources>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"
ControlsResourcesVersion="Version1"/>
</Application.Resources>
样式基础知识
使用样式将视觉属性设置提取到可重复使用的资源中。 以下示例显示了 3 个按钮,其样式设置了 BorderBrush、BorderThickness 和 Foreground 属性。 通过应用样式,可以使控件看起来相同,而不必在每个控件上分别设置这些属性。
可以在控件的 XAML 中定义内联样式,也可以将其作为可重复使用的资源。 在单个页面的 XAML 文件、App.xaml 文件或单独的资源字典 XAML 文件中定义资源。 资源字典 XAML 文件可跨应用程序共享,一个应用程序中可合并多个资源字典。 资源的定义位置决定了它的使用范围。 页面级资源只能在定义它们的页面中使用。 如果在 App.xaml 和页面中定义了具有相同键的资源,则页面中的资源优先于 App.xaml 中的资源。 如果资源在单独的资源词典文件中定义,则它的范围由引用资源词典的位置确定。
在 Style 定义中,需要一个 TargetType 属性和一个或多个 Setter 元素的集合。 TargetType 属性是一个指定要应用样式的 FrameworkElement 类型的字符串。 TargetType 值必须指定由 Windows Runtime 定义的 FrameworkElement 派生类型或引用程序集中可用的自定义类型。 如果尝试将样式应用到控件,而控件的类型与尝试应用的样式的 TargetType 属性不匹配,则会出现异常。
每个 Setter 元素都需要一个 Property 和一个 Value。 这些属性设置指明了该设置适用于哪些控制属性,以及为该属性设置的值。 可以使用属性或属性元素语法设置 Setter.Value。 这里的 XAML 显示了应用于之前显示的按钮的样式。 在此 XAML 中,前两个 Setter 元素使用特性语法,但是最后一个 Setter(用于设置 BorderBrush 属性)使用属性元素语法。 示例没有使用 x:Key attribute 属性,因此样式被隐式应用于按钮。 下一部分将对隐式或显式应用样式进行介绍。
<Page.Resources>
<Style TargetType="Button">
<Setter Property="BorderThickness" Value="5" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderBrush" >
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Red" Offset="0.25" />
<GradientStop Color="Blue" Offset="0.75" />
<GradientStop Color="LimeGreen" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<StackPanel Orientation="Horizontal">
<Button Content="Button"/>
<Button Content="Button"/>
<Button Content="Button"/>
</StackPanel>
应用隐式或显式样式
如果你将样式定义为资源,有两种方法可将其应用到控件:
- 隐式方法,通过仅指定 Style 的 TargetType。
- 显式方法,通过指定 Style 的 TargetType 和 x:Key 特性这一特性,然后通过使用显式键的 {StaticResource} 标记扩展引用设置目标控件的 Style 属性。
如果样式包含 x:Key 特性,则只能通过将控件的 Style 属性设置为键控样式,从而将其应用到控件。 相反,没有 x:Key 属性的样式会自动应用于其目标类型的每个控件,而这些控件并没有显式样式设置。
下面是两个按钮,分别展示了隐式和显式样式。
在本示例中,第一个样式具有 x:Key 特性,其目标类型为 Button。 第一个按钮的 Style 属性设置为此键,所以将显式应用该样式。 第二个样式会隐式应用于第二个按钮,因为其目标类型为 Button,并且该样式没有 x:Key 属性。
<Page.Resources>
<Style x:Key="PurpleStyle" TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="Purple"/>
</Style>
<Style TargetType="Button">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="25"/>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Green"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Foreground" Value="Green"/>
</Style>
</Page.Resources>
<Grid x:Name="LayoutRoot">
<Button Content="Button" Style="{StaticResource PurpleStyle}"/>
<Button Content="Button"/>
</Grid>
使用基于样式
为了使样式更易于维护并优化样式的重复使用,可以创建继承自其他样式的样式。 使用 BasedOn 属性创建继承的样式。 继承自其他样式的样式必须以相同类型的控件或从基本样式所针对的类型派生出来的控件为目标。 例如,如果基本样式的目标类型为 ContentControl,则基于此样式的样式可应用到 ContentControl 或从 ContentControl 派生的类型(如 Button 和 ScrollViewer)。 如果基于样式中没有设置值,则该值将从基本样式中继承。 要更改基本样式中的某个值,基于样式会覆盖该值。 下一个示例演示了具有从同一基本样式继承的样式的 Button 和 CheckBox。
基本样式以 ContentControl 为目标,并设置 Height 和 Width 属性。 基于此样式的样式应用到 CheckBox 和 Button,这些类型从 ContentControl 派生而来。 基于样式为 BorderBrush 和 Foreground 属性设置了不同的颜色。 (通常不会在 CheckBox 周围添加边框。我们在这里这样做是为了显示样式的效果。)
<Page.Resources>
<Style x:Key="BasicStyle" TargetType="ContentControl">
<Setter Property="Width" Value="130" />
<Setter Property="Height" Value="30" />
</Style>
<Style x:Key="ButtonStyle" TargetType="Button"
BasedOn="{StaticResource BasicStyle}">
<Setter Property="BorderBrush" Value="Orange" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Foreground" Value="Red" />
</Style>
<Style x:Key="CheckBoxStyle" TargetType="CheckBox"
BasedOn="{StaticResource BasicStyle}">
<Setter Property="BorderBrush" Value="Blue" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Foreground" Value="Green" />
</Style>
</Page.Resources>
<StackPanel>
<Button Content="Button" Style="{StaticResource ButtonStyle}" Margin="0,10"/>
<CheckBox Content="CheckBox" Style="{StaticResource CheckBoxStyle}"/>
</StackPanel>
使用工具轻松处理样式
为控件应用样式的快速方法是右键单击 Microsoft Visual Studio XAML 设计界面上的控件,然后选择“编辑样式”或“编辑模板”(取决于你右键单击的控件)。 然后,可以选择“应用资源”来应用现有样式,或选择“创建空”来定义新的样式。 如果创建了一个空样式,则可以选择在页面、App.xaml 文件或单独的资源字典中定义该样式。
轻型样式
通常在应用级别或页面级别上替代系统画笔,在任意情况下,颜色替代都将影响引用该画笔的所有控件;在 XAML 中,许多控件可以引用相同的系统画笔。
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackground" Color="Transparent"/>
<SolidColorBrush x:Key="ButtonForeground" Color="MediumSlateBlue"/>
<SolidColorBrush x:Key="ButtonBorderBrush" Color="MediumSlateBlue"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Page.Resources>
适用于 PointerOver(鼠标悬停在按钮上)、PointerPressed(按钮已被调用)或 Disabled(按钮不可交互)等状态。 这些词尾会附加到原始的轻量级样式名称上:ButtonBackgroundPointerOver、ButtonForegroundPressed、ButtonBorderBrushDisabled 等。修改这些画笔也将确保控件的颜色与应用主题保持一致。
在 App.Resources 级别放置这些画笔重载,将更改整个应用中的所有按钮,而不是单个页面上的按钮。
按控件样式设置
在其他情况下,只需更改一个页面上的单个控件,使其具有某种外观,而无需更改该控件的任何其他版本:
<CheckBox Content="Normal CheckBox" Margin="5"/>
<CheckBox Content="Special CheckBox" Margin="5">
<CheckBox.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="CheckBoxForegroundUnchecked"
Color="Purple"/>
<SolidColorBrush x:Key="CheckBoxForegroundChecked"
Color="Purple"/>
<SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked"
Color="White"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked"
Color="Purple"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked"
Color="Purple"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</CheckBox.Resources>
</CheckBox>
<CheckBox Content="Normal CheckBox" Margin="5"/>
这仅影响存在该控件的页面上的一个“特殊复选框”。
自定义控件
当你生成自己的自定义控件,并且这些控件的视觉效果和/或功能可能与我们的内置控件一致时,请考虑使用隐式样式和轻型样式资源来定义自定义内容。 你可以直接使用资源,也可以为资源创建新别名。
直接使用控件资源
例如,如果你正在编写一个看起来像 Button 的控件,可以让该控件直接引用按钮资源,如下所示:
<Style TargetType="local:MyCustomControl">
<Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
</Style>
为控件资源创建新别名
或者,如果你想创建自己的资源,则应将默认轻型样式资源的别名设置为这些自定义名称。
例如,自定义控件的样式可能具有特殊的资源定义:
<Style TargetType="local:MyCustomControl">
<Setter Property="Background" Value="{ThemeResource MyCustomControlBackground}" />
<Setter Property="BorderBrush" Value="{ThemeResource MyCustomControlBorderBrush}"/>
</Style>
在资源字典或主定义中,可以将轻型样式资源挂钩到自定义资源:
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
<StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
<StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
<StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
此代码要求重复使用 ThemeDictionary
三次,以正确处理三个不同的主题更改(Default
、Light
、HighContrast
)。
注意
如果为轻型样式资源分配新别名,并重新定义轻型样式资源,则在资源查找顺序不正确的情况下,可能不会应用你的自定义资源。 例如,如果在找到 MyCustomControlBackground
之前搜索的位置中替代 ButtonBackground
,替代将丢失。
避免重新设计控件样式
WinUI 2.2 或更高版本包含用于 WinUI 和系统控件的新样式和模板。
及时获取最新视觉样式的最佳方法是使用最新的 WinUI 2 包并避免使用自定义样式和模板(也称为重新创建模板)。 如需跨应用中的控件一致地应用某组值,样式仍不失为一种便捷的方式。 执行此操作时,务必使用我们的最新样式。
对于使用 WinUI 样式的系统控件(Windows.UI.Xaml.Controls
命名空间),请设置 BasedOn="{StaticResource Default<ControlName>Style}"
,其中 <ControlName>
是控件的名称。 例如:
<Style TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
对于 WinUI 2 控件(Microsoft.UI.Xaml.Controls
命名空间),默认样式在元数据中定义,因此省略 BasedOn
。
派生控件
如果从现有 XAML 控件派生自定义控件,默认情况下它不会获得 WinUI 2 样式。 要应用 WinUI 2 样式,请执行以下操作:
- 创建新的 Style,并将其 TargetType 设置为自定义控件。
- 使 Style 基于从其派生的控件的默认样式。
一种常见的情况是从 ContentDialog 派生新控件。 本示例演示如何创建将 DefaultContentDialogStyle
应用于自定义对话框的新 Style。
<ContentDialog
x:Class="ExampleApp.SignInContentDialog"
... >
<ContentDialog.Resources>
<Style TargetType="local:SignInContentDialog" BasedOn="{StaticResource DefaultContentDialogStyle}"/>
...
</ContentDialog.Resources>
<!-- CONTENT -->
</ContentDialog>
template 属性
样式 setter 可用于 Control 的 Template 属性,实际上,此样式构成了多数典型的 XAML 样式和应用的 XAML 资源。 此内容将在主题控件模板中更详细地讨论。