XAML 样式

可以使用 XAML 框架以多种方式自定义应用的外观。 使用样式可以设置控件属性,并重复使用这些设置,以便在多个控件中保持一致的外观。

样式基础知识

使用样式将视觉属性设置提取到可重用的资源中。 以下示例显示了 3 个按钮,其样式设置了 BorderBrushBorderThicknessForeground 属性。 通过应用样式,可以使控件看起来相同,而无需单独在每个控件上设置这些属性。

并排排列的三个样式按钮的屏幕截图。

您可以在 XAML 中为控件内联定义样式,或者将其定义为可重用的资源。 在单个页面的 XAML 文件中、App.xaml 文件或单独的资源字典 XAML 文件中定义资源。 资源字典 XAML 文件可以跨应用共享,多个资源字典可以在单个应用中合并。 定义资源的位置决定了可以使用该资源的范围。 页面级资源仅在定义它们的页面中可用。 如果在 App.xaml 和页面中定义了具有相同键的资源,则页面中的资源将覆盖 App.xaml 中的资源。 如果在单独的资源字典文件中定义资源,则其范围由引用资源字典的位置确定。

Style 定义中,需要一个 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>

应用隐式或显式样式

如果将样式定义为资源,可通过两种方法将其应用于控件:

如果样式包含 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 的类型,例如 ButtonScrollViewer。 如果基于样式中没有设置值,则该值将从基本样式中继承。 要更改基本样式中的某个值,基于样式会覆盖该值。 下一个示例显示了一个 按钮 和一个 复选框,它们的样式都继承自同一基本样式。

基于样式设置样式的样式按钮。

基样式以 ContentControl为目标,并设置 高度宽度 属性。 基于此样式的样式是以 CheckBoxButton为目标的,且派生自 ContentControl。 基于样式为 BorderBrushForeground 属性设置了不同的颜色。 (通常不会在 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>

轻便造型

通常在应用级别或页面级别上替代系统画笔,在任意情况下,颜色替代都将影响引用该画笔的所有控件;在 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(按钮不可交互)等状态。 这些词尾会附加到原始的轻量级样式名称上:ButtonBackgroundPointerOverButtonForegroundPressedButtonBorderBrushDisabled 等。修改这些画笔也将确保控件的颜色与应用主题保持一致。

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 三次,以正确处理三个不同的主题更改(DefaultLightHighContrast)。

谨慎

如果将轻量级样式资源分配给新的别名,并且重新定义轻量级样式资源,在资源查找顺序不正确时,您的自定义项可能无法应用。 例如,如果在找到 MyCustomControlBackground 之前搜索的位置中替代 ButtonBackground,替代将丢失。

避免对控件进行重整

保持最新视觉样式的最佳方法是避免自定义样式和模板(也称为重新模板化)。 样式仍然是在应用中跨控件一致地应用一组值的便捷方法。 执行此操作时,务必使用我们的最新样式。

对于 Microsoft.UI.Xaml.Controls 命名空间中提供具名默认样式的 WinUI 控件,请设置 BasedOn="{StaticResource Default<ControlName>Style}",其中 <ControlName> 是控件的名称。 这会保留控件的默认模板和视觉状态。 例如:

<Style TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
    <Setter Property="Foreground" Value="Blue"/>
</Style>

派生控件

如果从现有 XAML 控件派生自定义控件,则默认情况下它可能并不总是获取 WinUI 样式。 要应用 WinUI 样式:

  • 创建新的 Style,并将其 TargetType 设置为自定义控件。
  • 请依据您所派生控件的默认样式来设置样式。

一种常见的情况是从 ContentDialog 派生新控件。 此示例演示如何创建新的应用于自定义对话框的 DefaultContentDialogStyle 样式。

<ContentDialog
    x:Class="ExampleApp.SignInContentDialog"
    ... >

    <ContentDialog.Resources>
        <Style TargetType="local:SignInContentDialog" BasedOn="{StaticResource DefaultContentDialogStyle}"/>
        ...
    </ContentDialog.Resources> 
    <!-- CONTENT -->
</ContentDialog>        

模板属性

样式 setter 可用于 ControlTemplate 属性,实际上,此样式构成了多数典型的 XAML 样式和应用的 XAML 资源。 本主题中更详细地讨论了 控件模板