Day 7 - 复盘与巩固
建议用时:180-210 分钟
你将学会什么
- 把 Week 8 的样式、模板、组件、主题和状态打磨串起来。
- 能分清
Style、DataTemplate、ControlTemplate、UserControl的用途。 - 能重写一个带样式和数据模板的商品列表。
- 能说清主题文件拆分的目的。
- 能根据界面问题判断应该改样式、模板、组件还是 ViewModel 状态。
本周复盘重点是“界面结构怎么组织”。不要把 Style、DataTemplate、ControlTemplate、UserControl 混成同一个东西。
本页固定顺序
- 先学第一部分:弄懂今天最小、最重要的知识,并运行短例子。
- 再学第二部分:把刚学的知识组合成一个完整例子。
- 然后做第三部分:自己跟着敲,再完成重复训练和每日小测。
- 最后做第四部分:先独立完成作业,再用完整答案检查。
学习衔接
上一页学习的是“界面打磨”,今天继续学习“复盘与巩固”。先使用上一页已经会的写法,再只增加今天这个新知识点;如果前置内容还不能独立敲出,先回上一页复习,不要硬跳。
今天的最低通过线
第一次学习不要求背完整页。完成下面 3 项,就可以继续:
- 能用自己的话说明“复盘与巩固”解决什么问题。
- 把第一部分的短例子亲手敲完,并确认每个例子都能运行。
- 不看完整答案完成第三部分至少前 3 个例子,再主动改一个值观察结果。
第一部分:先学原理和最小知识
这一部分把 Week 8 的概念重新排成判断表。
1. 本周知识地图
| 日期 | 主题 | 解决的问题 |
|---|---|---|
| Day 1 | 样式体系 | 重复外观集中管理 |
| Day 2 | DataTemplate | 每条数据怎么显示 |
| Day 3 | ControlTemplate | 控件内部结构怎么改 |
| Day 4 | UserControl | 重复界面块怎么封装 |
| Day 5 | 主题结构 | 资源和样式文件怎么组织 |
| Day 6 | 界面打磨 | 空、加载、错误、禁用状态怎么表达 |
2. 四个容易混的概念
| 概念 | 负责什么 | 例子 |
|---|---|---|
Style | 改控件外观属性 | 按钮背景色、标题字号 |
DataTemplate | 定义数据项显示结构 | 商品显示名称、价格、状态 |
ControlTemplate | 重写控件内部结构 | 按钮内部用自定义 Border |
UserControl | 封装一块可复用界面 | ProductCard 商品卡片 |
3. 什么时候用哪个
| 需求 | 选择 |
|---|---|
| 多个按钮要同样颜色 | Style |
| 列表里的商品要显示三行 | DataTemplate |
| 按钮内部结构要完全改 | ControlTemplate |
| 多个页面都要同一块商品卡片 | UserControl |
| 多个页面共用颜色和间距 | 主题资源文件 |
| 没有数据时要提示 | ViewModel 状态 + IsVisible |
4. 本周最常见错误和修法
| 现象 | 原因 | 修法 |
|---|---|---|
| 样式不生效 | Classes 没写或 selector 写错 | 检查 Button.primary 和 Classes="primary" |
| 模板项不显示属性 | DataTemplate 没写正确类型 | 写 x:DataType="vm:ProductItem" |
| 按钮文字消失 | ControlTemplate 没有 ContentPresenter | 加 ContentPresenter |
| UserControl 参数不显示 | 内部没绑定自己的属性 | 使用 ElementName=Root |
| 主题文件找不到 | Include 路径错 | 检查 avares://ProductApp/Themes/... |
| 空状态不刷新 | 没通知 IsEmpty | 集合变化后调用 OnPropertyChanged(nameof(IsEmpty)) |
Week 8 界面复用与样式常用写法总表
| 主题 | 必须会的写法 |
|---|---|
| 资源 | StaticResource、x:Key |
| 样式 | Style Selector、Classes |
| 列表模板 | DataTemplate、x:DataType |
| 用户控件 | UserControl、xmlns:views |
| 主题结构 | 全局资源和局部资源分开 |
| 界面状态 | 空状态、保存中、错误提示、禁用按钮 |
| 控件提示 | PlaceholderText |
| 列表显示 | ItemsSource + ItemTemplate |
Week 8 的目标不是把界面做花,而是知道什么时候该复用、什么时候该提取、什么时候保持默认。
第二部分:把知识组合成完整例子
Week 8 的主线是:
资源管理重复值
-> Style 管理控件外观
-> DataTemplate 管理数据项显示
-> ControlTemplate 管理控件内部结构
-> UserControl 管理可复用界面块
-> ViewModel 状态控制空、加载、错误、禁用下面这个窗口复习资源、样式、DataTemplate 和空状态。
ViewModels/MainWindowViewModel.cs
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace ProductApp.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<ProductItem> Products { get; } = new()
{
new ProductItem { Name = "Keyboard", Price = 199, Enabled = true },
new ProductItem { Name = "Old Monitor", Price = 399, Enabled = false }
};
[ObservableProperty]
private string message = "商品列表已加载。";
public bool IsEmpty => Products.Count == 0;
[RelayCommand]
private void ClearProducts()
{
Products.Clear();
Message = "列表已清空。";
OnPropertyChanged(nameof(IsEmpty));
}
}
public sealed class ProductItem
{
public string Name { get; set; } = "";
public decimal Price { get; set; }
public bool Enabled { get; set; }
public string EnabledText => Enabled ? "启用" : "停用";
}Views/MainWindow.axaml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:ProductApp.ViewModels"
x:Class="ProductApp.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Width="680"
Height="460"
Title="Week 8 复盘">
<Window.Resources>
<SolidColorBrush x:Key="PrimaryBrush" Color="#2563EB" />
<SolidColorBrush x:Key="CardBorderBrush" Color="#DDDDDD" />
</Window.Resources>
<Window.Styles>
<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="12" />
</Style>
</Window.Styles>
<StackPanel Margin="20"
Spacing="12">
<TextBlock Classes="title"
Text="商品列表复盘" />
<TextBlock Text="暂无商品,请先新增数据。"
IsVisible="{Binding IsEmpty}" />
<ListBox ItemsSource="{Binding Products}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:ProductItem">
<Border Classes="card"
Margin="0,0,0,8">
<StackPanel Spacing="4">
<TextBlock Text="{Binding Name}"
FontWeight="Bold" />
<TextBlock Text="{Binding Price}" />
<TextBlock Text="{Binding EnabledText}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="清空列表"
Command="{Binding ClearProductsCommand}" />
<TextBlock Text="{Binding Message}"
TextWrapping="Wrap" />
</StackPanel>
</Window>先用一句话理解
资源提供颜色,样式统一标题和卡片,DataTemplate 定义每个商品怎么显示,IsEmpty 控制没有数据时的提示。
第三部分:跟着敲代码
从这里开始动手。今天只重写第一部分的复盘窗口。
步骤 1:写 ViewModel
使用第一部分的 ViewModels/MainWindowViewModel.cs。
重点看:
public bool IsEmpty => Products.Count == 0;它控制空状态是否显示。
步骤 2:写资源和样式
在 Window.Resources 放颜色,在 Window.Styles 放标题和卡片样式。
重点看:
<Style Selector="Border.card">这个样式会作用到带 Classes="card" 的 Border。
步骤 3:写 DataTemplate
重点看:
<DataTemplate x:DataType="vm:ProductItem">模板内部绑定的是当前商品,而不是整个 ViewModel。
步骤 4:验证空状态
点击 清空列表 后:
Products.Clear()清空集合。OnPropertyChanged(nameof(IsEmpty))通知空状态刷新。暂无商品,请先新增数据。显示出来。
小白重复敲写训练
复盘日从空白 XAML 重写三个视觉基础。
训练 1:样式类
<Style Selector="Button.primary">
<Setter Property="Background" Value="#2563EB" />
<Setter Property="Foreground" Value="White" />
</Style>训练 2:数据模板
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>训练 3:空状态
<TextBlock Text="暂无数据"
IsVisible="{Binding IsEmpty}"
HorizontalAlignment="Center" />把三段放进同一个列表页面,再改成自己的颜色和文案。
每日小测
做完本页后,用这 5 题检查是否真的掌握。
1. 判断题
本页的目标不是只把代码运行起来,还要能说清楚“为什么这样写”。
答案:对。能运行只是第一步,能解释原理、常用操作和常见错误,才说明本页内容进入了可复用能力。
2. 填空题
本页主题是:复盘与巩固。今天至少要掌握的 3 个点是:
1. 把 Week 8 的样式、模板、组件、主题和状态打磨串起来。
2. 能分清 `Style`、`DataTemplate`、`ControlTemplate`、`UserControl` 的用途。
3. 能重写一个带样式和数据模板的商品列表。答案:以上 3 点必须能用自己的代码跑通,不能只停留在阅读。
3. 流程题
遇到本页相关功能时,先按什么顺序处理?
答案:先看完整例子,确认最终效果;再读原理和名词;然后跟着第三部分从空项目敲代码;最后对照作业答案检查。
4. 找错误题
如果本页代码运行失败,第一步应该做什么?
答案:先看终端或 IDE 里的第一条错误,找到文件名和行号;不要同时改很多地方。再回到本页的“常见错误和修法”表格,对照错误类型逐项排查。
5. 改需求题
在本页完整例子跑通后,至少改一个小需求。
可选改法:
- 改一个字段名称。
- 多加一个校验条件。
- 多输出一行结果。
- 把固定数据改成用户输入。
- 把一次处理改成多条数据处理。
答案标准:修改后能重新运行,并能说明这次修改影响了哪一段逻辑。重点检查:把 Week 8 的样式、模板、组件、主题和状态打磨串起来。。
上位机专项练习
本周过关标准是能做出统一、可复用、状态明确的设备监控界面,而不是只会堆控件。
下面 3 个例子都要亲手敲。先运行原代码,再完成每个例子后面的改动任务。
专项例子 1:复习样式复用
<Button Classes="primary" Content="保存" />
<Button Classes="primary" Content="连接" />
<Button Classes="danger" Content="停止" />运行结果或界面效果:
同类操作统一,危险操作可区分改动任务: 检查三个按钮是否需要相同宽度。
专项例子 2:复习模板复用
<ItemsControl ItemsSource="{Binding Devices}">
<ItemsControl.ItemTemplate>
<DataTemplate><views:DeviceCard /></DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>运行结果或界面效果:
设备数量变化时自动生成卡片改动任务: 增加 WrapPanel 让卡片换行。
专项例子 3:复习可读状态
<TextBlock Text="离线 | 最后通信 10:32:15 | 点击重连"
Foreground="{DynamicResource OfflineBrush}" />运行结果或界面效果:
状态、时间和下一步同时可见改动任务: 为报警状态写一条同结构文字。
第四部分:作业完整答案
这一部分给出本周复盘作业的完整答案。
作业 1:写出 Week 8 主线
完整答案:
Week 8 主线:
把界面重复内容整理清楚。
资源负责重复值。
Style 负责控件外观。
DataTemplate 负责数据项显示。
ControlTemplate 负责控件内部结构。
UserControl 负责可复用界面块。
主题文件负责组织全局资源和样式。
ViewModel 状态负责空、加载、错误、禁用这些界面反馈。作业 2:概念选择题完整答案
| 需求 | 正确选择 |
|---|---|
| 所有主按钮统一蓝色 | Style |
| 商品列表每一项显示名称和价格 | DataTemplate |
| 按钮内部要自定义圆角 Border | ControlTemplate |
| 多个页面复用商品卡片 | UserControl |
| 全局主色统一管理 | Themes/Colors.axaml |
| 保存中显示进度条 | IsSaving + ProgressBar |
作业 3:排错题完整答案
| 问题 | 原因 | 修法 |
|---|---|---|
Button.primary 不生效 | 按钮没写 Classes="primary" | 给按钮加 class |
Name 绑定为空 | 模板内部数据类型不对或属性名错 | 检查 ProductItem.Name 和 x:DataType |
| UserControl 显示不出 Title | 内部绑定到外部 DataContext | 改成 ElementName=Root |
| 主题资源找不到 | ResourceInclude 路径错 | 检查 avares://ProductApp/... |
| 清空列表后空提示不显示 | 没通知 IsEmpty | 调用 OnPropertyChanged(nameof(IsEmpty)) |
作业 4:验收清单完整答案
Week 8 学完后,应能做到:
- 能写全局资源和样式。
- 能用
Classes套用样式。 - 能用
DataTemplate自定义列表项。 - 能解释模板内部绑定的是当前项。
- 能写一个简单
ControlTemplate并保留ContentPresenter。 - 能写一个带
StyledProperty的 UserControl。 - 能用
ResourceInclude和StyleInclude拆分主题。 - 能用
IsEmpty、IsSaving、ErrorMessage表示界面状态。 - 能判断界面问题应该改样式、模板、组件还是状态。
本周真正要带走的能力
重复外观 -> Style
重复值 -> Resource
重复数据项显示 -> DataTemplate
重复控件内部结构 -> ControlTemplate
重复界面块 -> UserControl
重复全局视觉规则 -> Theme files
界面状态不清楚 -> ViewModel 状态属性能按这张表判断问题,界面代码就不会越写越乱。