🎯 前言:我在学什么?

最近我在学习 WPF(Windows 桌面应用开发),老师提到了一个叫 MVVM 的设计模式。一开始我完全听不懂,各种术语满天飞:“ViewModel”、“绑定”、“命令”、“通知”……头都大了!

但后来我发现,只要用对工具(比如 CommunityToolkit.Mvvm),其实 MVVM 并没有那么难

今天这篇笔记,就用最简单的话,带你一步步看懂一个完整的 WPF + MVVM 小例子,并重点讲清楚:

ObservableObject 到底是什么?它为什么这么重要?


🧱 第一步:项目结构长什么样?

我们有两个核心文件:

  1. MainWindow.xaml:界面(View)
  2. MainWindowViewModel.cs:数据和逻辑(ViewModel)

它们通过“数据绑定”连接在一起,互不干扰,这就是 MVVM 的核心思想


🔧 第二步:ViewModel 代码详解(C# 部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfToolKitDemo.ViewModels
{
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
int _age = 18;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
string _firstName = "Albert";

[ObservableProperty]
string _lastName = "North";

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ChangeAgeCommand))]
bool _isActive;

public string FullName => $"{FirstName} {LastName}";

[RelayCommand(CanExecute = nameof(IsActive))]
void ChangeAge()
{
Age = 22;
}

[RelayCommand]
void ChangeFirstName()
{
FirstName = "AlbertPlus";
LastName = "LunaNorth";
}
}
}

🌟 重点1:为什么要继承 ObservableObject

1
public partial class MainWindowViewModel : ObservableObject

一句话解释:

继承 ObservableObject 是为了让界面(View)能“自动知道数据变了”,然后自动更新显示。

📌 举个例子:

你有一个文本框显示 Age,初始是 18
当你在代码里把 Age = 22; 时,界面能不能自动变成 22?

  • 如果不继承 ObservableObject,就不能。
  • 继承了它,并配合 [ObservableProperty],就可以!

👉 所以:**ObservableObject 是 MVVM 中实现“自动刷新界面”的基础!**


🌟 重点2:[ObservableProperty] 是什么?

1
2
[ObservableProperty]
int _age = 18;

一句话解释:

这个 [ObservableProperty] 是一个“魔法标签”,它会自动生成属性 + 自动通知界面更新

比如上面这行代码,Toolkit 会自动帮你生成:

1
2
3
4
5
6
7
8
9
10
11
12
public int Age
{
get => _age;
set
{
if (_age != value)
{
_age = value;
OnPropertyChanged(nameof(Age)); // 自动通知界面
}
}
}

👉 你不用自己写这些重复代码,省时省力!


🌟 重点3:[NotifyPropertyChangedFor] 是啥?

1
2
3
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
string _firstName = "Albert";

一句话解释:

_firstName 改变时,不仅要通知 FirstName 更新,还要通知 FullName 更新!

因为 FullName 是由 FirstName + LastName 拼出来的:

1
public string FullName => $"{FirstName} {LastName}";

如果名字变了,全名也应该变。这个特性就是告诉程序:“别忘了我也要刷新!”


🌟 重点4:[NotifyCanExecuteChangedFor]CanExecute

1
2
3
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ChangeAgeCommand))]
bool _isActive;
1
2
3
4
5
[RelayCommand(CanExecute = nameof(IsActive))]
void ChangeAge()
{
Age = 22;
}

一句话解释:

这个组合用来控制“按钮能不能点”。

  • CanExecute = nameof(IsActive):表示 ChangeAge 这个命令能不能执行,取决于 IsActive 属性。
  • _isActive 变化时,用 [NotifyCanExecuteChangedFor] 告诉命令:“你去看看还能不能执行”。

📌 举个例子:

  • IsActive = true → 按钮可用 ✅
  • IsActive = false → 按钮变灰 ❌

这就是 WPF 中“按钮启用/禁用”的常见做法。


🌟 重点5:[RelayCommand] 是命令的快捷方式

1
2
3
4
5
6
[RelayCommand]
void ChangeFirstName()
{
FirstName = "AlbertPlus";
LastName = "LunaNorth";
}

一句话解释:

不用手动 new RelayCommand(…),加个标签就能把方法变成“可绑定的命令”。

生成后,你可以在 XAML 中这样用:

1
<Button Command="{Binding ChangeFirstNameCommand}" />

注意:方法名是 ChangeFirstName,但命令名自动变成 ChangeFirstNameCommand(Toolkit 自动加 Command 后缀)!


🖼️ 第三步:XAML 界面绑定(界面部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<Window x:Class="WpfToolKitDemo.MainWindow"
...
xmlns:viewmodels="clr-namespace:WpfToolKitDemo.ViewModels"
Title="MainWindow" Height="450" Width="800">

<!-- 设置数据上下文 -->
<Window.DataContext>
<viewmodels:MainWindowViewModel />
</Window.DataContext>

<StackPanel>
<TextBlock Text="{Binding Age}"/>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
<TextBlock Text="{Binding FullName}"/>
<CheckBox IsChecked="{Binding IsActive}"/>
<Button Command="{Binding ChangeAgeCommand}" Content="ChangeAge"/>
<Button Command="{Binding ChangeFirstNameCommand}" Content="FirstName"/>
</StackPanel>
</Window>

🔍 绑定说明:

控件 绑定内容 作用
TextBlock {Binding Age} 显示年龄
CheckBox {Binding IsActive} 控制按钮是否可用
Button {Binding ChangeAgeCommand} 点击执行 ChangeAge 方法

只要你在 ViewModel 里改了数据,界面就会自动刷新!


✅ 实际运行效果预测

操作 效果
启动程序 显示 Age=18, FirstName=Albert, FullName=Albert North
点击 “ChangeAge” 按钮 Age 变成 22(前提是 CheckBox 勾选)
点击 “FirstName” 按钮 名字变成 AlbertPlus,姓变成 LunaNorth,全名也更新
勾选/取消 CheckBox “ChangeAge” 按钮随之启用/禁用

🧠 总结:ObservableObject 详解(重点回顾)

问题 回答
ObservableObject 是什么? 是 CommunityToolkit 提供的一个基类,用于支持属性变更通知
为什么要继承它? 让 ViewModel 能通知界面“数据变了,请刷新”
它是怎么工作的? 结合 [ObservableProperty] 特性,自动生成 OnPropertyChanged 调用
不继承会怎样? 界面不会自动更新,必须手动写通知代码或刷新
它解决了什么问题? 解耦了界面和逻辑,实现了“数据驱动界面”

🎁 给初学者的小建议

  1. 先模仿:把这段代码复制到你的项目中,运行看看效果。
  2. 再修改:试着改名字、改年龄、加新属性,观察界面变化。
  3. 理解原理:记住一句话:“数据变了,通知界面”
  4. 善用 Toolkit[ObservableProperty][RelayCommand] 是你的好帮手,别怕用!

📚 参考资料


🏁 结语

MVVM 看似复杂,但一旦你理解了 ObservableObject 和数据绑定的本质,就会发现:

原来,让界面自动更新,也可以这么简单!

希望这篇笔记能帮你迈出 WPF MVVM 的第一步 🚶‍♂️

继续加油,你一定能学会!


📌 笔记名称:WPF MVVM 入门学习笔记:从零开始理解 CommunityToolkit 与 ObservableObject 详解


如果你觉得这篇笔记有用,可以收藏起来,以后忘了就回来看看 😊
也欢迎分享给同样在学 WPF 的小伙伴!