XAML 中的主题资源是一组应用不同值的资源,具体取决于哪个系统主题处于活动状态。 XAML 框架支持 3 个主题:“浅色”、“深色”和“HighContrast”。
先决条件:本主题假定已读取 ResourceDictionary 和 XAML 资源引用。
主题资源 v. 静态资源
有两个 XAML 标记扩展可以从现有的 XAML 资源字典引用 XAML 资源: {StaticResource} 标记扩展 和 {ThemeResource} 标记扩展。
当应用加载时以及随后每次在运行时主题更改时,都会对 {ThemeResource} 标记扩展 进行评估。 这通常是用户更改其设备设置或应用内更改当前主题的编程更改的结果。
相比之下,仅当应用首次加载 XAML 时,才会评估 {StaticResource} 标记扩展 。 它不会更新。 这类似于在 XAML 中查找和替换应用启动时的实际运行时值。
资源字典结构中的主题资源
每个主题资源都是 XAML 文件 themeresources.xaml 的一部分。 出于设计目的,themeresources.xaml 在 Windows 软件开发工具包 (SDK) 安装中的 \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic 文件夹中可用。 themeresources.xaml 中的资源字典也在同一目录中的 generic.xaml 中重现。
Windows 运行时不使用这些物理文件进行运行时查找。 因此,它们专门位于 DesignTime 文件夹中,默认情况下不会复制到应用中。 相反,这些资源字典存在于内存中作为 Windows 运行时本身的一部分,并且应用的 XAML 资源引用在运行时解析为主题资源(或系统资源)。
自定义主题资源的指南
定义和使用自己的自定义主题资源时,请遵循以下准则:
除了“HighContrast”字典外,还要为“浅色”和“深色”指定主题字典。 虽然可以使用“Default”作为键创建 ResourceDictionary ,但最好是显式的,而是使用“Light”、“Dark”和“HighContrast”。
在:样式、Setter、控件模板、属性资源库和动画中使用 {ThemeResource} 标记扩展 。
请勿在 ThemeDictionaries 中的资源定义中使用 {ThemeResource} 标记扩展。 请改用 {StaticResource} 标记扩展 。
异常:可以使用 {ThemeResource} 标记扩展 引用与 ThemeDictionaries 中的应用主题无关的资源。 这些资源的示例包括主题色资源(如
SystemAccentColor或系统颜色资源),这些资源通常以“SystemColor”为SystemColorButtonFaceColor前缀。
注意
如果不遵循这些准则,你可能会在应用中看到与主题相关的意外行为。 有关详细信息,请参阅 “故障排除主题资源 ”部分。
XAML 颜色渐变和与主题相关的画笔
“浅色”、“深色”和“HighContrast”主题的组合颜色集构成了 XAML 中的 Windows 颜色渐变 。 无论是要修改系统主题还是将主题应用于自己的 XAML 元素,请务必了解颜色资源的结构。
有关如何在 Windows 应用中应用颜色的其他信息,请参阅 Windows 应用中的颜色。
浅色和深色主题颜色
XAML 框架提供了一组命名 的颜色 资源,其中包含为“浅色”和“深色”主题定制的值。 对于 WinUI 2,主题资源在 通用主题资源 Xaml 文件中定义。 颜色名称非常描述其预期用法,并且每个颜色资源都有相应的 SolidColorBrush 资源。
小窍门
有关这些颜色的可视概述,请参阅 WinUI 3 库应用: 颜色
WinUI 3 库应用包含大多数 WinUI 3 控件、特性和功能的交互式示例。 从 Microsoft 应用商店获取应用或在 GitHub 上获取源代码
Windows 系统对比度主题颜色
除了 XAML 框架提供的资源集外,还有一组派生自 Windows 系统调色板的颜色值。 这些颜色不特定于 Windows 运行时或 Windows 应用。 但是,许多 XAML 画笔 资源在使用“HighContrast”主题运行系统(并且应用正在运行)时使用这些颜色。 XAML 框架将这些系统范围的颜色作为键控资源提供。 键遵循命名格式: SystemColor[name]Color.
有关支持对比度主题的详细信息,请参阅 对比度主题。
系统主题色
除了系统对比度主题颜色之外,系统主题颜色还以特殊颜色资源的形式提供,使用键 SystemAccentColor。 在运行时,此资源获取用户在 Windows 个性化设置中指定为主题色的颜色。
注释
虽然可以替代系统颜色资源,但最佳做法是尊重用户的颜色选择,尤其是对对比度主题设置。
与主题相关的画笔
前面各节中显示的颜色资源用于在系统主题资源字典中设置 SolidColorBrush 资源的 Color 属性。 使用画笔资源将颜色应用于 XAML 元素。
让我们看看此画笔的颜色值如何在运行时确定。 在“浅色”和“深色”资源字典中,此画笔的定义如下:
<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{StaticResource TextFillColorPrimary}"/>
在“HighContrast”资源字典中,此画笔的定义如下:
<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>
将此画笔应用于 XAML 元素时,其颜色由当前主题在运行时确定,如下表所示。
| 主题 | 颜色资源 | 运行时值 |
|---|---|---|
| Light | TextFillColorPrimary | #E4000000 |
| 黑暗 | TextFillColorPrimary | #FFFFFFFF |
| HighContrast | SystemColorWindowTextColor | 在“文本”设置中指定的颜色。 |
XAML 类型渐变
themeresources.xaml 文件定义了多个资源,这些资源定义了一个样式,这些 样式 可以应用于 UI 中的文本容器,特别是针对 TextBlock 或 RichTextBlock。 这些不是默认的隐式样式。 提供它们是为了方便你创建与字体指南中记录的 Windows 类型渐变匹配的 XAML UI 定义。
这些样式适用于要应用于整个文本容器的文本属性。 如果希望仅应用于文本部分的样式,请在容器中的文本元素上设置属性,例如在 TextBlock.Inlines 中运行或在 RichTextBlock.Blocks 中的 Paragraph 上设置属性。
应用于 TextBlock 时,样式如下所示:
| Style | Weight | 尺寸 |
|---|---|---|
| 标题 | 定期 | 12 |
| Body | 定期 | 14 |
| 身体强 | 半曲 | 14 |
| 正文大 | 定期 | 18 |
| 字幕 | 半曲 | 20 |
| Title | 半曲 | 28 |
| 标题大 | 半曲 | 40 |
| Display | 半曲 | 68 |
<TextBlock Text="Caption" Style="{StaticResource CaptionTextBlockStyle}"/>
<TextBlock Text="Body" Style="{StaticResource BodyTextBlockStyle}"/>
<TextBlock Text="Body Strong" Style="{StaticResource BodyStrongTextBlockStyle}"/>
<TextBlock Text="Body Large" Style="{StaticResource BodyLargeTextBlockStyle}"/>
<TextBlock Text="Subtitle" Style="{StaticResource SubtitleTextBlockStyle}"/>
<TextBlock Text="Title" Style="{StaticResource TitleTextBlockStyle}"/>
<TextBlock Text="Title Large" Style="{StaticResource TitleLargeTextBlockStyle}"/>
<TextBlock Text="Display" Style="{StaticResource DisplayTextBlockStyle}"/>
有关如何在应用中使用 Windows 类型渐变的指导,请参阅 Windows 应用中的版式。
有关 XAML 样式的详细信息,请参阅 GitHub 上的 WinUI:
小窍门
有关这些样式的直观概述,请参阅 WinUI 3 库应用: 版式
BaseRichTextBlockStyle
TargetType: RichTextBlock
为所有其他 RichTextBlock 容器样式提供通用属性。
<!-- Usage -->
<RichTextBlock Style="{StaticResource BaseRichTextBlockStyle}">
<Paragraph>Rich text.</Paragraph>
</RichTextBlock>
<!-- Style definition -->
<Style x:Key="BaseRichTextBlockStyle" TargetType="RichTextBlock">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextTrimming" Value="None"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="LineStackingStrategy" Value="MaxHeight"/>
<Setter Property="TextLineBounds" Value="Full"/>
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
</Style>
BodyRichTextBlockStyle
<!-- Usage -->
<RichTextBlock Style="{StaticResource BodyRichTextBlockStyle}">
<Paragraph>Rich text.</Paragraph>
</RichTextBlock>
<!-- Style definition -->
<Style x:Key="BodyRichTextBlockStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaseRichTextBlockStyle}">
<Setter Property="FontWeight" Value="Normal"/>
</Style>
注意: RichTextBlock 样式没有 TextBlock 执行的所有文本渐变样式,这主要是因为 RichTextBlock 的基于块的文档对象模型使得在单个文本元素上设置属性更容易。 此外,使用 XAML 内容属性设置 TextBlock.Text 会引入没有要设置样式的文本元素的情况,因此必须设置容器的样式。 这不是 RichTextBlock 的问题,因为其文本内容始终必须位于特定文本元素(如 Paragraph)中,而该元素可用于页面标题、页面子标题和类似的文本渐变定义。
其他命名样式
还有一组额外的键式 样式 定义,可以应用于 按钮 的样式与默认隐式样式不同。
NavigationBackButtonNormalStyle
TargetType: Button
此 样式 为 按钮 提供了一个完整的模板,该模板可以是导航应用的导航后退按钮。 默认尺寸为 40 x 40 像素。 若要定制样式,可以显式设置 Button 上的 Height、Width、FontSize 和其他属性,或使用 BasedOn 创建派生样式。
下面是一个 按钮 ,其中应用了 NavigationBackButtonNormalStyle 资源。
<Button Style="{StaticResource NavigationBackButtonNormalStyle}" />
它类似于:
NavigationBackButtonSmallStyle
TargetType: Button
此 样式 为 按钮 提供了一个完整的模板,该模板可以是导航应用的导航后退按钮。 它类似于 NavigationBackButtonNormalStyle,但其尺寸为 30 x 30 像素。
下面是一个 按钮 ,其中应用了 NavigationBackButtonSmallStyle 资源。
<Button Style="{StaticResource NavigationBackButtonSmallStyle}" />
主题资源疑难解答
如果不遵循 使用主题资源的准则,你可能会在应用中看到与主题相关的意外行为。
例如,打开浅色主题浮出控件时,深色主题应用的一部分也会更改,就像它们位于浅色主题中一样。 或者,如果你导航到浅色主题页面,然后导航回去,则原始深色主题页面(或部分页面)现在看起来就像在浅色主题中一样。
通常,当你提供“默认”主题和“HighContrast”主题来支持高对比度方案时,会出现这些类型的问题,然后在应用的不同部分中同时使用“浅色”和“深色”主题。
例如,请考虑此主题字典定义:
<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
直观地说,这看起来是正确的。 你想要更改高 myBrush 对比度时所指向的颜色,但如果不是高对比度,则依赖于 {ThemeResource} 标记扩展 来确保指向 myBrush 主题的正确颜色。 如果应用从未在其可视化树中的元素上设置 FrameworkElement.RequestedTheme ,这通常按预期工作。 但是,在开始重新主题可视化树的不同部分后,就会在应用中遇到问题。
之所以出现问题,是因为画笔是共享资源,与大多数其他 XAML 类型不同。 如果 XAML 子树中有 2 个元素具有引用相同画笔资源的不同主题,则当框架遍查每个子树以更新其 {ThemeResource} 标记扩展 表达式时,共享画笔资源的更改将反映在其他子树中,这不是预期结果。
若要解决此问题,请将“默认”字典替换为“浅色”和“深色”主题的单独主题字典,以及“HighContrast”:
<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
但是,如果在继承的属性(如 Foreground)中引用了其中任何资源,仍会出现问题。 自定义控件模板可能会使用 {ThemeResource} 标记扩展指定元素的前景色,但是当框架将继承的值传播到子元素时,它提供对由 {ThemeResource} 标记扩展表达式解析的资源的直接引用。 这会导致框架处理主题更改时出现问题,因为它会遍视控件的可视化树。 它会重新评估 {ThemeResource} 标记扩展表达式以获取新的画笔资源,但尚未将此引用传播到控件的子级;这种情况稍后发生,例如在下一个度量值传递期间。
因此,在对控件可视化树进行响应主题更改后,框架将演练子级并更新在其属性上设置的任何 {ThemeResource} 标记扩展 表达式,或更新在其属性上设置的对象。 这是问题发生的地方;框架会演练画笔资源,因为它使用 {ThemeResource} 标记扩展指定其颜色,因此会重新评估它。
此时,框架似乎已经污染了主题词典,因为它现在有一个字典中的资源,该字典中设置了另一个字典中的颜色。
若要解决此问题,请使用 {StaticResource} 标记扩展 而不是 {ThemeResource} 标记扩展。 应用准则后,主题字典如下所示:
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
请注意, {ThemeResource} 标记扩展 仍在“HighContrast”字典中使用,而不是 {StaticResource} 标记扩展。 这种情况在指南前面给出的例外情况下。 用于“HighContrast”主题的大多数画笔值都使用由系统全局控制的颜色选择,但以专门命名的资源(名称中以“SystemColor”为前缀)向 XAML 公开。 系统允许用户通过“轻松访问中心”设置其对比度主题设置时应使用的特定颜色。 这些颜色选项将应用于专门命名的资源。 XAML 框架使用相同的主题更改事件在检测到系统级别更改时更新这些画笔。 这就是为什么在此处使用 {ThemeResource} 标记扩展的原因。