Day 1 - 样式体系
建议用时:210-240 分钟
你将学会什么
- 知道
Resources是可复用的值。 - 知道
Styles是可复用的外观规则。 - 会在
App.axaml里定义全局颜色资源。 - 会在
App.axaml里定义全局按钮和标题样式。 - 会用
Classes给控件套用指定样式。 - 会判断什么时候放全局,什么时候放当前页面。
本页只讲样式体系基础:资源、样式、选择器、样式类、作用范围。DataTemplate、ControlTemplate、UserControl 会在后面几天单独讲。
本页固定顺序
- 先学第一部分:弄懂今天最小、最重要的知识,并运行短例子。
- 再学第二部分:把刚学的知识组合成一个完整例子。
- 然后做第三部分:自己跟着敲,再完成重复训练和每日小测。
- 最后做第四部分:先独立完成作业,再用完整答案检查。
学习衔接
上一页学习的是“复盘与巩固”,今天继续学习“样式体系”。先使用上一页已经会的写法,再只增加今天这个新知识点;如果前置内容还不能独立敲出,先回上一页复习,不要硬跳。
今天的最低通过线
第一次学习不要求背完整页。完成下面 3 项,就可以继续:
- 能用自己的话说明“样式体系”解决什么问题。
- 把第一部分的短例子亲手敲完,并确认每个例子都能运行。
- 不看完整答案完成第三部分至少前 3 个例子,再主动改一个值观察结果。
第一部分:先学原理和最小知识
这一部分从最小知识开始。先读解释,再把紧跟着的短例子敲一遍。样式体系的核心不是让界面复杂,而是让重复外观集中管理。
1. 为什么需要样式体系
如果每个按钮都这样写:
<Button Background="#2563EB"
Foreground="White"
Padding="12,6"
Content="保存" />页面能运行,但问题是:
- 颜色重复。
- 内边距重复。
- 多个页面要改同一种按钮时,需要到处找。
- 有的按钮漏改后,界面不统一。
样式体系就是把这些重复内容集中起来。
2. Resource 是什么
资源是可复用的值。
<SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" />这里定义了一个名字叫 PrimaryBrush 的颜色画刷。
使用时:
Foreground="{StaticResource PrimaryBrush}"意思是从资源里取出 PrimaryBrush。
3. Style 是什么
样式是一组外观规则。
<Style Selector="Button.primary">
<Setter Property="Background" Value="{StaticResource PrimaryBrush}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="12,6" />
</Style>这条规则表示:带 primary 类的按钮,使用指定背景、文字颜色和内边距。
4. Selector 是什么
Selector 决定样式作用到哪些控件。
| Selector | 含义 |
|---|---|
Button | 所有按钮 |
Button.primary | 带 primary 类的按钮 |
Button.danger | 带 danger 类的按钮 |
TextBlock.title | 带 title 类的文字 |
Border.card | 带 card 类的边框区域 |
本页推荐先用 Button.primary 这种写法,不要一开始就写 Button 影响所有按钮。
5. Classes 是什么
Classes 是给控件贴样式标签。
<Button Classes="primary" Content="保存" />如果全局有:
<Style Selector="Button.primary">这个按钮就会套用该样式。
没有写 Classes="primary" 的按钮不会套用这条样式。
6. Application.Resources 和 Window.Resources 的区别
| 位置 | 作用范围 | 适合放什么 |
|---|---|---|
Application.Resources | 整个应用 | 全局颜色、全局尺寸 |
Window.Resources | 当前窗口 | 当前页面专用资源 |
某个容器的 .Resources | 当前容器内部 | 某一块局部资源 |
如果只有一个页面用,就先放 Window.Resources。如果多个页面都要用,再放到 App.axaml。
7. Application.Styles 和 Window.Styles 的区别
| 位置 | 作用范围 | 适合场景 |
|---|---|---|
Application.Styles | 全应用 | 主按钮、危险按钮、标题样式 |
Window.Styles | 当前窗口 | 当前页面特殊样式 |
StackPanel.Styles | 当前容器内部 | 局部实验或小区域样式 |
全局样式要谨慎。放全局意味着所有页面都可能用到它。
8. 样式和业务逻辑的边界
样式只管外观,不管业务。
适合放样式:
- 背景色。
- 字号。
- 字体粗细。
- 内边距。
- 边框。
不适合放样式:
- 保存逻辑。
- 删除规则。
- 价格校验。
- 列表数据。
业务逻辑仍然放 ViewModel 或 Service。
样式体系常用写法速查
功能练习优先用默认样式;只有出现重复外观时,再提取资源或样式。
| 需求 | 写法 | 说明 |
|---|---|---|
| 定义资源 | <SolidColorBrush x:Key="PrimaryBrush" ... /> | 给值起名字 |
| 引用资源 | {StaticResource PrimaryBrush} | 使用资源 |
| 定义样式 | <Style Selector="Button.primary"> | 命中指定按钮 |
| 使用样式类 | Classes="primary" | 套用样式 |
| 控件类型选择器 | Style Selector="TextBlock" | 影响所有 TextBlock |
| class 选择器 | Style Selector="Button.primary" | 只影响指定 class |
| 全局样式 | App.axaml | 全应用可用 |
| 局部样式 | 当前窗口资源 | 只影响当前页面 |
判断顺序:
能用默认样式 -> 先不加样式
多个地方重复值 -> 提取资源
多个控件重复外观 -> 提取 Style第二部分:把知识组合成完整例子
今天要做的是全局样式:把颜色和按钮外观放到 App.axaml,然后在 MainWindow.axaml 里直接使用。
App.axaml
把下面内容放到 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>
<SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" />
<SolidColorBrush x:Key="DangerBrush" Color="#DC2626" />
<SolidColorBrush x:Key="CardBorderBrush" Color="#DDDDDD" />
</Application.Resources>
<Application.Styles>
<FluentTheme />
<Style Selector="TextBlock.title">
<Setter Property="FontSize" Value="24" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="{StaticResource PrimaryBrush}" />
</Style>
<Style Selector="Border.card">
<Setter Property="BorderBrush" Value="{StaticResource CardBorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="16" />
</Style>
<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>
</Application.Styles>
</Application>Views/MainWindow.axaml
把下面内容放到 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="20"
Spacing="12">
<TextBlock Classes="title"
Text="商品管理" />
<Border Classes="card">
<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 定义全局资源和样式,页面控件通过 Classes="primary"、Classes="title"、Classes="card" 选择要使用的样式。
第三部分:跟着敲代码
从这里开始动手。今天会改两个文件:App.axaml 和 Views/MainWindow.axaml。
例子 1:先定义全局颜色资源
在 App.axaml 里添加:
<Application.Resources>
<SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" />
<SolidColorBrush x:Key="DangerBrush" Color="#DC2626" />
<SolidColorBrush x:Key="CardBorderBrush" Color="#DDDDDD" />
</Application.Resources>这一步只定义资源,还没有样式。
例子 2:定义主按钮样式
在 Application.Styles 里保留模板自带的 <FluentTheme />,再添加:
<Style Selector="Button.primary">
<Setter Property="Background" Value="{StaticResource PrimaryBrush}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="12,6" />
</Style>注意:FluentTheme 不要删,它是 Avalonia 默认主题。
例子 3:页面里使用样式类
在 MainWindow.axaml 里写:
<Button Classes="primary"
Content="保存" />这个按钮会使用全局 Button.primary 样式。
例子 4:完整页面
完整代码使用第一部分的 App.axaml 和 Views/MainWindow.axaml。
常见错误和修法
| 错误 | 为什么错 | 修法 |
|---|---|---|
| 一开始就写大量样式 | 容易掩盖功能问题 | 先让功能可用,再抽样式 |
| 选择器写错 | 样式不会生效 | 先从简单控件选择器开始 |
| 样式影响范围太大 | 其他页面也被改到 | 用类选择器或局部资源缩小范围 |
| 样式里写业务状态 | 逻辑和外观混在一起 | 业务状态放 ViewModel |
| 忘记保留默认可读性 | 外观变了但难用 | 优先保证文字、间距、状态清楚 |
小白重复敲写训练
样式体系要通过“同一个样式控制多个控件”来记。
训练 1:按控件类型设置样式
<Window.Styles>
<Style Selector="Button">
<Setter Property="Padding" Value="14,8" />
<Setter Property="Margin" Value="4" />
</Style>
</Window.Styles>放三个按钮,确认都受到影响。
训练 2:使用样式类
<Style Selector="Button.danger">
<Setter Property="Background" Value="#DC2626" />
<Setter Property="Foreground" Value="White" />
</Style>
<Button Classes="danger" Content="删除" />改动任务:再定义 primary 类。
训练 3:组合选择器状态
<Style Selector="Button.danger:pointerover">
<Setter Property="Background" Value="#B91C1C" />
</Style>第三遍分别测试普通、悬停和禁用状态。
每日小测
做完本页后,用这 5 题检查是否真的掌握。
1. 判断题
本页的目标不是只把代码运行起来,还要能说清楚“为什么这样写”。
答案:对。能运行只是第一步,能解释原理、常用操作和常见错误,才说明本页内容进入了可复用能力。
2. 填空题
本页主题是:样式体系。今天至少要掌握的 3 个点是:
1. 知道 `Resources` 是可复用的值。
2. 知道 `Styles` 是可复用的外观规则。
3. 会在 `App.axaml` 里定义全局颜色资源。答案:以上 3 点必须能用自己的代码跑通,不能只停留在阅读。
3. 流程题
遇到本页相关功能时,先按什么顺序处理?
答案:先看完整例子,确认最终效果;再读原理和名词;然后跟着第三部分从空项目敲代码;最后对照作业答案检查。
4. 找错误题
如果本页代码运行失败,第一步应该做什么?
答案:先看终端或 IDE 里的第一条错误,找到文件名和行号;不要同时改很多地方。再回到本页的“常见错误和修法”表格,对照错误类型逐项排查。
5. 改需求题
在本页完整例子跑通后,至少改一个小需求。
可选改法:
- 改一个字段名称。
- 多加一个校验条件。
- 多输出一行结果。
- 把固定数据改成用户输入。
- 把一次处理改成多条数据处理。
答案标准:修改后能重新运行,并能说明这次修改影响了哪一段逻辑。重点检查:知道 Resources 是可复用的值。。
上位机专项练习
状态样式必须一眼可辨,但不能只依赖颜色。文字、图标和颜色要一起表达在线、离线与报警。
下面 3 个例子都要亲手敲。先运行原代码,再完成每个例子后面的改动任务。
专项例子 1:在线状态样式
<Style Selector="Border.online">
<Setter Property="Background" Value="#E8F5E9" />
<Setter Property="BorderBrush" Value="#2E7D32" />
</Style>
<Border Classes="online" Padding="10"><TextBlock Text="在线" /></Border>运行结果或界面效果:
绿色底色加“在线”文字改动任务: 增加离线灰色样式。
专项例子 2:报警状态不只靠红色
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="!" FontWeight="Bold" />
<TextBlock Text="高温报警:85.2 C" />
</StackPanel>运行结果或界面效果:
符号和文字共同提示报警改动任务: 增加报警时间。
专项例子 3:用伪类切换样式
<Style Selector="Button:disabled">
<Setter Property="Opacity" Value="0.45" />
</Style>
<Button Content="开始采集" IsEnabled="{Binding CanStart}" />运行结果或界面效果:
不可用按钮视觉上变淡改动任务: 为 pointerover 增加背景变化。
第四部分:作业完整答案
这一部分给出当天作业的完整答案。先照第三部分敲一遍,再用这里检查结果。
作业要求
做一个全局样式练习:
- 在
App.axaml定义PrimaryBrush。 - 在
App.axaml定义DangerBrush。 - 在
App.axaml定义Button.primary。 - 在
App.axaml定义Button.danger。 - 在
MainWindow.axaml使用Classes="primary"。 - 在
MainWindow.axaml使用Classes="danger"。 - 普通取消按钮不加样式类,保持默认外观。
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>
<SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" />
<SolidColorBrush x:Key="DangerBrush" Color="#DC2626" />
<SolidColorBrush x:Key="CardBorderBrush" Color="#DDDDDD" />
</Application.Resources>
<Application.Styles>
<FluentTheme />
<Style Selector="TextBlock.title">
<Setter Property="FontSize" Value="24" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="{StaticResource PrimaryBrush}" />
</Style>
<Style Selector="Border.card">
<Setter Property="BorderBrush" Value="{StaticResource CardBorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="16" />
</Style>
<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>
</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="20"
Spacing="12">
<TextBlock Classes="title"
Text="商品管理" />
<Border Classes="card">
<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>必须验证的结果
| 控件 | 应该看到 |
|---|---|
| 标题 | 使用 TextBlock.title 的字号和主色 |
| 保存按钮 | 使用主按钮样式 |
| 删除按钮 | 使用危险按钮样式 |
| 取消按钮 | 保持默认按钮样式 |
| 卡片区域 | 有边框和内边距 |
如果样式不生效,按这个顺序检查
- 检查
App.axaml里是否保留<FluentTheme />。 - 检查资源名是否一致,比如
PrimaryBrush不能写成PrimaryColor。 - 检查样式选择器是否是
Button.primary。 - 检查按钮是否写了
Classes="primary"。 - 检查样式是否放在
Application.Styles内。
今天真正要掌握的闭环
Application.Resources 定义可复用值
-> Application.Styles 定义全局外观规则
-> Selector 决定样式命中哪些控件
-> Classes 给控件选择样式
-> 页面不再重复写同样颜色和内边距样式体系的目标是减少重复,不是让页面变复杂。