可以使用 XAML 框架以多种方式自定义应用的外观。 使用样式可以设置控件属性,并重复使用这些设置,以便在多个控件中保持一致的外观。
WinUI 和样式设计
从 WinUI 2.2 开始,我们已使用 WinUI 跨 UI 组件提供新的视觉样式更新。 如果你注意到 UI 未更新到最新样式,请确保更新到最新的 WinUI NuGet 包。
从 WinUI 2.6 开始,我们为大多数控件提供新样式,以及一个新版本系统,让你在需要时还原到以前的控件样式。 我们鼓励你使用新样式,因为它们更符合 Windows 的设计方向。 但是,如果方案不支持新样式,则以前的版本仍可用。
使用 WinUI 版本 2 时,可以通过在 ControlsResourcesVersion
上设置 XamlControlsResources
属性,并将其包含在 Application.Resources
中,从而更改样式版本。 参数 ControlsResourcesVersion
默认为枚举值 Version2
。
将此值设置为 Version1
加载 XamlControlsResources
以前的样式版本,而不是最新 WinUI 版本使用的新样式。 不支持在运行时更改此属性,并且 VisualStudio 的热重载功能将不起作用;但是,重新生成应用程序后,将看到控件样式更改。
<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 中的资源。 如果在单独的资源字典文件中定义资源,则其范围由引用资源字典的位置确定。
在 样式 定义中,需要一个 TargetType 属性和一个或多个 Setter 元素的集合。 TargetType 属性是一个字符串,用于指定 FrameworkElement 类型以应用样式。 TargetType 值必须指定由 Windows 运行时定义的 FrameworkElement派生类型或引用程序集中可用的自定义类型。 如果尝试向控件应用样式,并且控件的类型与尝试应用的样式的 TargetType 属性不匹配,则会发生异常。
每个 Setter 元素都需要一个 属性 和一个 值。 这些属性设置指示设置适用的控件属性,以及要为该属性设置的值。 可以使用特性语法或属性元素语法设置 Setter.Value。 此处的 XAML 显示应用于前面显示的按钮的样式。 在此 XAML 中,前两个 Setter 元素使用属性语法,但最后一个用于 BorderBrush 属性的 Setter使用的是属性元素语法。 该示例不使用 x:Key 属性 属性,因此样式将隐式应用于按钮。 下一部分将介绍隐式或显式应用样式。
<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>
应用隐式或显式样式
如果将样式定义为资源,可通过两种方法将其应用于控件:
- 通过隐式指定 TargetType 来实现 样式。
- 通过为 样式 指定 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。 如果未在基于样式中设置值,则会从基本样式继承该值。 若要从基本样式更改值,继承样式会覆盖该值。 下一个示例显示了一个 按钮 和一个 复选框,它们的样式都继承自同一基本样式。
基样式以 ContentControl为目标,并设置 高度和 宽度 属性。 基于此样式的样式是以 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>
使用工具轻松处理样式
向控件应用样式的快速方法是右键单击 Visual Studio XAML 设计图面 Microsoft上的控件,然后选择 编辑样式 或 编辑模板(具体取决于您右键单击的控件)。 然后,可以通过选择 “应用资源 ”来应用现有样式,也可以通过选择“ 创建空”来定义新样式。 如果创建空样式,则可以选择在页面、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(已调用按钮)或“禁用”(按钮不可交互)。 这些结尾附加到原始轻型样式名称上: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"/>
这只会影响该控件所在的页面上的一个“特殊 CheckBox”。
自定义控件
在构建自己的自定义控件时,这些控件在视觉上和/或功能上可能与内置控件保持一致,请考虑使用隐式样式和轻型样式资源来定义自定义内容。 可以直接使用资源,也可以为资源创建新的别名。
直接使用控制资源
例如,如果要编写类似于 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
)。
谨慎
如果将轻量级样式资源分配给新的别名,并且重新定义轻量级样式资源,在资源查找顺序不正确时,您的自定义项可能无法应用。 例如,如果在找到 ButtonBackground
之前覆盖在某个搜索到的位置上的 MyCustomControlBackground
,则覆盖操作将被错过。
避免对控件进行重整
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 样式:
- 创建一个新的 样式,并将其 TargetType 设置为你的自定义控件。
- 请依据您所派生控件的默认样式来设置样式。
这种情况下,一个常见的方案是从 ContentDialog派生新控件。 此示例演示如何创建新的应用于自定义对话框的 DefaultContentDialogStyle
样式。
<ContentDialog
x:Class="ExampleApp.SignInContentDialog"
... >
<ContentDialog.Resources>
<Style TargetType="local:SignInContentDialog" BasedOn="{StaticResource DefaultContentDialogStyle}"/>
...
</ContentDialog.Resources>
<!-- CONTENT -->
</ContentDialog>
模板属性
样式设置器可用于 控件的 模板 属性,事实上,这构成了典型 XAML 样式和应用 XAML 资源的大部分。 本主题中更详细地讨论了 控件模板。