Skip to Content
Week 07Day 7 - 复盘与巩固

Day 7 - 复盘与巩固

建议用时:180-210 分钟

你将学会什么

  • 把 Week 7 的 MVVM 知识连成一条主线。
  • 能解释 ObservablePropertyRelayCommandCanExecuteObservableCollection 的作用。
  • 能重写一个 MVVM 表单小窗口。
  • 能根据报错判断是绑定问题、命令问题还是通知问题。
  • 能说清 code-behind 事件和 MVVM 命令的区别。

复盘不是背概念。复盘要能做到:写得出、跑得通、说得清、能排错。

本页固定顺序

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

学习衔接

上一页学习的是“小项目:MVVM CRUD”,今天继续学习“复盘与巩固”。先使用上一页已经会的写法,再只增加今天这个新知识点;如果前置内容还不能独立敲出,先回上一页复习,不要硬跳。

今天的最低通过线

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

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

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

这一部分把 Week 7 的内容重新排成一张图。

1. 本周知识地图

日期主题解决的问题必须掌握
Day 1MVVM 入门View 和 ViewModel 分工BindingDataContext
Day 2绑定与转换原始数据变成显示数据OneWayTwoWay、计算属性、Converter
Day 3命令机制按钮动作放进 ViewModel[RelayCommand]CanExecute
Day 4表单验证保存前拦住错误输入字段错误、TryParseValidateAll
Day 5集合绑定列表自动刷新ObservableCollectionItemsSourceSelectedItem
Day 6MVVM CRUD把前面知识串成项目新增、读取、更新、删除

2. MVVM 三层职责

负责什么不该做什么
View布局、控件、绑定大量业务规则
ViewModel页面状态、命令、验证、提示直接操作具体控件
Model业务数据结构界面显示细节

如果你在 ViewModel 里写 NameBox.Text,说明职责混了。ViewModel 应该写 ProductName,View 通过绑定连接到它。

3. 本周最重要的 8 个关键词

关键词直接解释
DataContextView 绑定时使用的数据对象
x:DataType告诉 XAML 当前绑定的 ViewModel 类型
[ObservableProperty]自动生成可通知界面的属性
[NotifyPropertyChangedFor]一个属性变化时,通知另一个计算属性也变化
[RelayCommand]自动生成可绑定的命令
CanExecute判断命令当前能不能执行
ObservableCollection新增删除时能通知界面的集合
SelectedItem列表当前选中的那一项

4. 最常见错误和修法

现象常见原因修法
绑定不显示DataContext 没设置检查 App.axaml.cs
属性名绑定报错XAML 名字写错对照 ViewModel 真实属性名
按钮命令找不到方法没写 [RelayCommand]给方法加特性并检查命名
按钮状态不刷新没写 NotifyCanExecuteChangedFor给影响条件的字段加通知
计算属性不刷新没通知计算属性使用 NotifyPropertyChangedFor 或手动 OnPropertyChanged
列表新增不刷新用了普通 List<T>改用 ObservableCollection<T>
选中项为空时报错没判断 SelectedItem先判断是否为 null

5. 判断代码放哪里

需求放哪里
按钮文字View
输入框绑定哪个属性View
商品名称当前值ViewModel
保存按钮能不能点ViewModel
保存命令执行什么ViewModel
商品数据结构Model 或 ViewModel 附近的简单类
真实保存到数据库Service

Week 7 MVVM 常用写法总表

主题必须会的写法
属性通知[ObservableProperty]
命令[RelayCommand]
属性联动[NotifyPropertyChangedFor]
文本绑定Text="{Binding ..., Mode=TwoWay}"
命令绑定Command="{Binding SaveCommand}"
列表绑定ItemsSource="{Binding Products}"
选中项SelectedItem="{Binding SelectedProduct}"
集合刷新ObservableCollection<T>
表单验证TryParse + ErrorMessage
CRUDAdd、Select、Edit、Remove、Search

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

Week 7 的主线是:

ViewModel 保存状态 -> View 用 Binding 显示和修改状态 -> Button 用 Command 触发动作 -> ViewModel 做验证和处理 -> View 自动显示处理结果

下面这个窗口复习属性绑定、命令、验证和按钮可执行条件。

ViewModels/MainWindowViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; namespace ProductApp.ViewModels; public partial class MainWindowViewModel : ViewModelBase { [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(SaveCommand))] private string productName = "Keyboard"; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(SaveCommand))] private string priceText = "199"; [ObservableProperty] private string message = "修改商品信息后点击保存。"; public string StatusText => string.IsNullOrWhiteSpace(ProductName) ? "名称未填写" : $"当前商品:{ProductName.Trim()}"; partial void OnProductNameChanged(string value) { OnPropertyChanged(nameof(StatusText)); } private bool CanSave() { return !string.IsNullOrWhiteSpace(ProductName) && decimal.TryParse(PriceText, out decimal price) && price > 0; } [RelayCommand(CanExecute = nameof(CanSave))] private void Save() { decimal price = decimal.Parse(PriceText.Trim()); Message = $"保存成功:{ProductName.Trim()},价格 {price}"; } }

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="560" Height="340" Title="Week 7 复盘"> <StackPanel Margin="20" Spacing="10"> <TextBlock Text="{Binding StatusText}" FontSize="20" FontWeight="Bold" /> <TextBlock Text="商品名称" /> <TextBox Text="{Binding ProductName, Mode=TwoWay}" /> <TextBlock Text="商品价格" /> <TextBox Text="{Binding PriceText, Mode=TwoWay}" /> <Button Content="保存" Command="{Binding SaveCommand}" /> <TextBlock Text="{Binding Message}" TextWrapping="Wrap" /> </StackPanel> </Window>

先用一句话理解

输入框改 ViewModel 属性,属性变化刷新状态文字和保存按钮状态,按钮执行命令,命令修改 Message,界面自动显示结果。

第三部分:跟着敲代码

从这里开始动手。今天只重做一个复盘窗口,目标是确认 MVVM 核心链路已经掌握。

步骤 1:确认 App.axaml.cs 设置了 DataContext

模板里应该有:

desktop.MainWindow = new MainWindow { DataContext = new MainWindowViewModel(), };

没有这段,绑定就不知道去哪里找 ViewModel。

步骤 2:写 ViewModel

使用第一部分的 ViewModels/MainWindowViewModel.cs

你要特别看这几行:

[NotifyCanExecuteChangedFor(nameof(SaveCommand))] private string productName = "Keyboard";

它表示:ProductName 改变时,保存按钮要重新判断能不能点。

步骤 3:写 View

使用第一部分的 Views/MainWindow.axaml

你要特别看这几行:

<TextBox Text="{Binding ProductName, Mode=TwoWay}" /> <Button Content="保存" Command="{Binding SaveCommand}" />

第一行是双向绑定,第二行是命令绑定。

步骤 4:运行并做错误输入

必须做这几次验证:

  1. 清空商品名称。
  2. 把价格改成 abc
  3. 把价格改成 0
  4. 输入 Mouse99 后保存。

如果前三个输入时保存按钮不可点,说明 CanExecute 生效。

小白重复敲写训练

复盘日要独立写出 MVVM 三条最重要的闭环。

训练 1:属性绑定

[ObservableProperty] private string _title = "商品管理";
<TextBlock Text="{Binding Title}" />

训练 2:命令绑定

[RelayCommand] private void Clear() => Title = "";
<Button Content="清空" Command="{Binding ClearCommand}" />

训练 3:集合绑定

public ObservableCollection<string> Items { get; } = new(); [RelayCommand] private void Add() => Items.Add($"Item {Items.Count + 1}");
<ListBox ItemsSource="{Binding Items}" />

把三段组合到同一个页面,再关掉答案重写一次。

每日小测

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

1. 判断题

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

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

2. 填空题

本页主题是:复盘与巩固。今天至少要掌握的 3 个点是:

1. 把 Week 7 的 MVVM 知识连成一条主线。 2. 能解释 `ObservableProperty`、`RelayCommand`、`CanExecute`、`ObservableCollection` 的作用。 3. 能重写一个 MVVM 表单小窗口。

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

3. 流程题

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

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

4. 找错误题

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

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

5. 改需求题

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

可选改法:

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

答案标准:修改后能重新运行,并能说明这次修改影响了哪一段逻辑。重点检查:把 Week 7 的 MVVM 知识连成一条主线。。

上位机专项练习

本周过关标准是:界面只负责展示和绑定,状态与动作主要放在 ViewModel。

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

专项例子 1:复习属性通知

[ObservableProperty] private double temperature; private void UpdateTemperature(double value) { Temperature = value; }

运行结果或界面效果:

Temperature 改变后绑定界面自动刷新

改动任务: 增加 Pressure 属性。

专项例子 2:复习命令

[RelayCommand] private void StartCollecting() { StatusText = "正在采集"; }

运行结果或界面效果:

按钮通过 StartCollectingCommand 启动采集

改动任务: 增加 StopCollectingCommand。

专项例子 3:复习集合

Devices.Add(new DeviceItem("PLC-03", "在线")); Alarms.Add(new AlarmItem("PLC-03", "高温"));

运行结果或界面效果:

设备列表和报警列表都自动增加一项

改动任务: 删除第一条已确认报警。

第四部分:作业完整答案

这一部分给出本周复盘作业的完整答案。

作业 1:写出 Week 7 主线

完整答案:

Week 7 主线: ViewModel 保存页面状态。 View 用 Binding 显示和修改状态。 Button 用 Command 触发 ViewModel 方法。 ViewModel 用 CanExecute 控制按钮能不能执行。 ViewModel 做验证并更新 Message。 集合页面用 ObservableCollection 通知列表新增删除。 列表页面用 SelectedItem 表示当前选中项。

作业 2:解释 6 个核心写法

完整答案:

写法解释
[ObservableProperty]自动生成属性,并在属性变化时通知界面
[RelayCommand]自动生成按钮可以绑定的命令
Mode=TwoWay输入框变化会写回 ViewModel
CanExecute决定命令现在能不能执行
ObservableCollection集合新增删除时通知界面刷新
SelectedItem当前选中的列表项

作业 3:排错题完整答案

问题原因修法
SaveCommand 不存在Save 方法没加 [RelayCommand]添加 [RelayCommand]
输入框改了但保存按钮仍禁用没通知命令重新判断添加 [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
StatusText 不刷新计算属性没有变化通知调用 OnPropertyChanged(nameof(StatusText))
列表新增后不显示没用 ObservableCollection改成 ObservableCollection<T>
选中项删除时报错没判断 SelectedProduct 是否为空删除前判断 SelectedProduct is null

作业 4:验收清单完整答案

Week 7 学完后,应能做到:

  1. 能写出 MainWindowViewModel 并让窗口绑定它。
  2. 能用 [ObservableProperty] 写可通知属性。
  3. 能用 TextBox 双向绑定 ViewModel 属性。
  4. 能用 [RelayCommand] 让按钮执行 ViewModel 方法。
  5. 能用 CanExecute 禁用不该执行的按钮。
  6. 能写名称和价格验证。
  7. 能用 ObservableCollection 绑定列表。
  8. 能用 SelectedItem 显示当前详情。
  9. 能把新增、更新、删除放进命令。
  10. 能根据错误信息判断是绑定、命令、通知还是集合问题。

本周真正要带走的能力

看到一个 Avalonia 页面需求 -> 先定义 ViewModel 状态 -> 再让 View 绑定状态 -> 按钮动作写成命令 -> 输入错误写成验证 -> 列表数据用 ObservableCollection -> 当前项用 SelectedItem -> 最后把新增、保存、删除串成闭环

这就是进入实际 Avalonia 项目之前必须掌握的 MVVM 基础线。