Skip to Content
Week 08Day 5 - 主题结构

Day 5 - 主题结构

建议用时:240-270 分钟

你将学会什么

  • 知道为什么要拆分主题文件。
  • 会创建 Themes/Colors.axaml 保存颜色资源。
  • 会创建 Themes/Spacing.axaml 保存间距资源。
  • 会创建 Themes/Buttons.axaml 保存按钮样式。
  • 会在 App.axaml 里用 ResourceIncludeStyleInclude 引入主题文件。
  • 会在页面里使用主题资源和样式类。

主题结构不是为了多建文件,而是为了让颜色、间距、按钮样式有固定位置。以后改主题时,不需要到每个页面里找散落的颜色和边距。

本页固定顺序

  1. 先学第一部分:弄懂今天最小、最重要的知识,并运行短例子。
  2. 再学第二部分:把刚学的知识组合成一个完整例子。
  3. 然后做第三部分:自己跟着敲,再完成重复训练和每日小测。
  4. 最后做第四部分:先独立完成作业,再用完整答案检查。

学习衔接

上一页学习的是“UserControl 复用”,今天继续学习“主题结构”。先使用上一页已经会的写法,再只增加今天这个新知识点;如果前置内容还不能独立敲出,先回上一页复习,不要硬跳。

今天的最低通过线

第一次学习不要求背完整页。完成下面 3 项,就可以继续:

  • 能用自己的话说明“主题结构”解决什么问题。
  • 把第一部分的短例子亲手敲完,并确认每个例子都能运行。
  • 不看完整答案完成第三部分至少前 3 个例子,再主动改一个值观察结果。

第一部分:先学原理和最小知识

这一部分从最小知识开始。先读解释,再把紧跟着的短例子敲一遍。主题结构解决的是“资源和样式越来越多以后放在哪里”的问题。

1. 为什么要拆分主题文件

如果所有资源和样式都放在 App.axaml,一开始能用,但后面会变得很长:

颜色 间距 字号 按钮 输入框 卡片 列表

拆分后,每个文件职责清楚:

文件负责什么
Colors.axaml颜色画刷
Spacing.axaml页面边距、卡片内边距
Buttons.axaml按钮样式
App.axaml引入主题文件

2. ResourceDictionary 是什么

ResourceDictionary 是资源字典,用来放可复用值。

<ResourceDictionary> <SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" /> </ResourceDictionary>

页面使用:

Foreground="{StaticResource PrimaryBrush}"

3. ResourceInclude 是什么

ResourceInclude 用来把外部资源字典引入应用。

<ResourceInclude Source="avares://ProductApp/Themes/Colors.axaml" />

avares://ProductApp/... 的意思是从当前 Avalonia 应用资源里找这个文件。

4. StyleInclude 是什么

StyleInclude 用来引入外部样式文件。

<StyleInclude Source="avares://ProductApp/Themes/Buttons.axaml" />

样式文件的根节点是:

<Styles>

资源文件的根节点是:

<ResourceDictionary>

这两个不要写反。

5. 为什么 FluentTheme 不能删

模板自带:

<FluentTheme />

它提供 Avalonia 默认控件主题。如果删掉,很多控件默认外观会出问题。

正确做法是在它下面追加自己的样式:

<Application.Styles> <FluentTheme /> <StyleInclude Source="avares://ProductApp/Themes/Buttons.axaml" /> </Application.Styles>

6. 什么应该放主题文件

适合放:

  • 全局颜色。
  • 全局间距。
  • 全局按钮样式。
  • 多个页面共用的卡片样式。

不适合放:

  • 某个页面只用一次的临时颜色。
  • 某个控件的业务数据。
  • 保存、删除、校验逻辑。

7. 修改主题时的顺序

如果要换主色,不要去每个页面改按钮。

正确顺序:

  1. 打开 Themes/Colors.axaml
  2. 修改 PrimaryBrush 的颜色。
  3. 运行应用。
  4. 检查所有使用 PrimaryBrushButton.primary 的地方是否一起变化。

主题结构常用写法速查

需求写法说明
全局主题App.axaml应用级资源
颜色资源SolidColorBrush统一颜色
间距资源x:Double统一间距
控件样式Style Selector="Button.primary"统一外观
页面局部资源Window.Resources只给当前页面
引用资源{StaticResource Key}使用资源

主题结构原则:

默认样式够用就不改 重复值再提取 全局通用放 App.axaml 页面专用放页面资源

第二部分:把知识组合成完整例子

今天要把主题拆成这些文件:

Themes/ Colors.axaml Spacing.axaml Buttons.axaml App.axaml Views/MainWindow.axaml

Themes/Colors.axaml

<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" /> <SolidColorBrush x:Key="DangerBrush" Color="#DC2626" /> <SolidColorBrush x:Key="CardBorderBrush" Color="#DDDDDD" /> </ResourceDictionary>

Themes/Spacing.axaml

<ResourceDictionary xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Thickness x:Key="PageMargin">20</Thickness> <Thickness x:Key="CardPadding">16</Thickness> </ResourceDictionary>

Themes/Buttons.axaml

<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Style Selector="Button.primary"> <Setter Property="Background" Value="{StaticResource PrimaryBrush}" /> <Setter Property="Foreground" Value="White" /> <Setter Property="Padding" Value="12,6" /> </Style> <Style Selector="Button.danger"> <Setter Property="Background" Value="{StaticResource DangerBrush}" /> <Setter Property="Foreground" Value="White" /> <Setter Property="Padding" Value="12,6" /> </Style> </Styles>

App.axaml

<Application xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ProductApp.App" RequestedThemeVariant="Default"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceInclude Source="avares://ProductApp/Themes/Colors.axaml" /> <ResourceInclude Source="avares://ProductApp/Themes/Spacing.axaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> <Application.Styles> <FluentTheme /> <StyleInclude Source="avares://ProductApp/Themes/Buttons.axaml" /> </Application.Styles> </Application>

Views/MainWindow.axaml

<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ProductApp.Views.MainWindow" Width="620" Height="360" Title="主题结构"> <StackPanel Margin="{StaticResource PageMargin}" Spacing="12"> <TextBlock Text="商品管理" FontSize="24" FontWeight="Bold" Foreground="{StaticResource PrimaryBrush}" /> <Border BorderBrush="{StaticResource CardBorderBrush}" BorderThickness="1" Padding="{StaticResource CardPadding}"> <StackPanel Spacing="8"> <TextBlock Text="名称:Keyboard" /> <TextBlock Text="价格:199" /> <StackPanel Orientation="Horizontal" Spacing="8"> <Button Classes="primary" Content="保存" /> <Button Content="取消" /> <Button Classes="danger" Content="删除" /> </StackPanel> </StackPanel> </Border> </StackPanel> </Window>

先用一句话理解

颜色和间距放进资源字典,按钮样式放进样式文件,App.axaml 统一引入,页面只使用资源和样式类。

第三部分:跟着敲代码

从这里开始动手。今天会创建 Themes 文件夹。

步骤 1:创建 Themes 文件夹

在项目根目录创建:

Themes/

然后创建:

Themes/Colors.axaml Themes/Spacing.axaml Themes/Buttons.axaml

步骤 2:写 Colors.axaml

使用第一部分的 Themes/Colors.axaml

步骤 3:写 Spacing.axaml

使用第一部分的 Themes/Spacing.axaml

步骤 4:写 Buttons.axaml

使用第一部分的 Themes/Buttons.axaml

步骤 5:修改 App.axaml

使用第一部分的 App.axaml,确认:

<FluentTheme />

仍然存在。

步骤 6:修改 MainWindow.axaml

使用第一部分的 Views/MainWindow.axaml,确认页面没有直接写散落主色,而是使用:

Foreground="{StaticResource PrimaryBrush}"

和:

Classes="primary"

常见错误和修法

错误为什么错修法
主题里写具体业务文字主题应该管外观,不管业务文字放页面或 ViewModel
颜色到处写死后期换主题困难集中放资源变量
资源 key 命名混乱查找和复用困难用清楚名称,例如 PrimaryBrush
一次改太多主题项很难定位错误先改颜色,再改字号,再改间距
忽略默认主题控件状态可能变差先基于默认样式小步调整

小白重复敲写训练

主题训练只改资源值,不改每个按钮本身。

训练 1:定义颜色资源

<Application.Resources> <SolidColorBrush x:Key="PageBackgroundBrush" Color="#F8FAFC" /> <SolidColorBrush x:Key="TextBrush" Color="#111827" /> <SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" /> </Application.Resources>

训练 2:使用动态资源

<Border Background="{DynamicResource PageBackgroundBrush}"> <TextBlock Text="商品管理" Foreground="{DynamicResource TextBrush}" /> </Border>

改动资源颜色,观察使用它的控件一起变化。

训练 3:切换一个资源

Application.Current!.Resources["PageBackgroundBrush"] = new SolidColorBrush(Color.Parse("#111827"));

第三遍同时切换文字颜色,避免深色背景上看不清。

每日小测

做完本页后,用这 5 题检查是否真的掌握。

1. 判断题

本页的目标不是只把代码运行起来,还要能说清楚“为什么这样写”。

答案:对。能运行只是第一步,能解释原理、常用操作和常见错误,才说明本页内容进入了可复用能力。

2. 填空题

本页主题是:主题结构。今天至少要掌握的 3 个点是:

1. 知道为什么要拆分主题文件。 2. 会创建 `Themes/Colors.axaml` 保存颜色资源。 3. 会创建 `Themes/Spacing.axaml` 保存间距资源。

答案:以上 3 点必须能用自己的代码跑通,不能只停留在阅读。

3. 流程题

遇到本页相关功能时,先按什么顺序处理?

答案:先看完整例子,确认最终效果;再读原理和名词;然后跟着第三部分从空项目敲代码;最后对照作业答案检查。

4. 找错误题

如果本页代码运行失败,第一步应该做什么?

答案:先看终端或 IDE 里的第一条错误,找到文件名和行号;不要同时改很多地方。再回到本页的“常见错误和修法”表格,对照错误类型逐项排查。

5. 改需求题

在本页完整例子跑通后,至少改一个小需求。

可选改法:

  • 改一个字段名称。
  • 多加一个校验条件。
  • 多输出一行结果。
  • 把固定数据改成用户输入。
  • 把一次处理改成多条数据处理。

答案标准:修改后能重新运行,并能说明这次修改影响了哪一段逻辑。重点检查:知道为什么要拆分主题文件。。

上位机专项练习

主题资源集中管理颜色、间距和字号,让白天模式、夜间模式或客户配色可以整体切换。

下面 3 个例子都要亲手敲。先运行原代码,再完成每个例子后面的改动任务。

专项例子 1:定义语义颜色

<Color x:Key="OnlineColor">#2E7D32</Color> <Color x:Key="AlarmColor">#C62828</Color> <Color x:Key="SurfaceColor">#F5F7FA</Color>

运行结果或界面效果:

颜色名称表达用途而不是具体色值

改动任务: 增加 OfflineColor。

专项例子 2:使用动态资源

<Border Background="{DynamicResource SurfaceBrush}"> <TextBlock Foreground="{DynamicResource PrimaryTextBrush}" Text="设备监控" /> </Border>

运行结果或界面效果:

切换主题时资源可以更新

改动任务: 为报警文字使用 AlarmBrush。

专项例子 3:设置主题变体

Application.Current!.RequestedThemeVariant = ThemeVariant.Dark;

运行结果或界面效果:

应用切换为深色主题

改动任务: 增加一个按钮在 Light 和 Dark 间切换。

第四部分:作业完整答案

这一部分给出当天作业的完整答案。完整代码就是第一部分所有文件。下面补充检查答案。

作业要求

建立一个主题结构:

  1. Colors.axaml 保存 PrimaryBrushDangerBrushCardBorderBrush
  2. Spacing.axaml 保存 PageMarginCardPadding
  3. Buttons.axaml 保存 Button.primaryButton.danger
  4. App.axaml 使用 ResourceInclude 引入资源。
  5. App.axaml 使用 StyleInclude 引入按钮样式。
  6. MainWindow.axaml 使用这些资源和样式。

文件结构完整答案

ProductApp/ App.axaml Themes/ Colors.axaml Spacing.axaml Buttons.axaml Views/ MainWindow.axaml

必须验证的结果

检查项应该看到
PrimaryBrush标题使用主色
Button.primary保存按钮使用主按钮样式
Button.danger删除按钮使用危险按钮样式
PageMargin页面外边距来自资源
CardPadding卡片内边距来自资源

如果报错,按这个顺序检查

  1. 检查 Themes 文件夹和文件名是否完全一致。
  2. 检查 ResourceInclude 路径是否是 avares://ProductApp/Themes/Colors.axaml
  3. 检查资源文件根节点是否是 ResourceDictionary
  4. 检查样式文件根节点是否是 Styles
  5. 检查 App.axaml 是否保留了 <FluentTheme />
  6. 检查资源 key 是否一致,比如 PageMargin 不能写成 PagePadding

今天真正要掌握的闭环

Themes 文件夹拆分资源和样式 -> ResourceInclude 引入资源字典 -> StyleInclude 引入样式文件 -> 页面通过 StaticResource 和 Classes 使用主题 -> 修改主题文件,全局一起变化

主题结构的价值是让视觉规则有固定位置,而不是散落在每个页面里。