✅ WPF 系列第二篇:MVVM 模式与 CommunityToolkit.Mvvm
一、什么是 MVVM?
MVVM(Model-View-ViewModel)是一种架构模式,特别适合 WPF 应用程序开发。它将应用程序分为三个主要部分:
┌─────────────┐
│ View │ ← XAML 界面,负责展示
│ (XAML) │
└──────┬──────┘
│ DataBinding
↓
┌─────────────┐
│ ViewModel │ ← 业务逻辑,连接 View 和 Model
│ (C#) │
└──────┬──────┘
│
↓
┌─────────────┐
│ Model │ ← 数据模型,业务数据
│ (C#) │
└─────────────┘
MVVM 的优势
- 分离关注点:界面逻辑与业务逻辑分离
- 易于测试:ViewModel 可以独立测试
- 代码复用:同一个 ViewModel 可以绑定到不同的 View
- 数据绑定:通过数据绑定实现自动更新
二、安装 CommunityToolkit.Mvvm 和配置项目结构
2.1 安装 CommunityToolkit.Mvvm
微软社区提供的 CommunityToolkit.Mvvm 包提供了强大的 MVVM 工具,包括:
- [ObservableProperty] – 自动生成属性通知
- [RelayCommand] – 自动生成命令
- ObservableObject – 可观察对象基类
- 更多实用特性…
安装步骤:
在 Visual Studio 的 NuGet 包管理器中搜索 CommunityToolkit.Mvvm 并安装。

2.2 MVVM 文件夹结构
让我们将项目组织成标准的 MVVM 结构:
WPF_DEMO/
├── Views/ # 视图层(XAML 文件)
│ └── MainWindow.xaml
│ └── MainWindow.xaml.cs
├── ViewModels/ # 视图模型层(业务逻辑)
│ └── MainWindowViewModel.cs
└── Models/ # 数据模型层
└── User.cs

创建文件夹结构:
三、核心特性详解
3.1 [ObservableProperty] 特性
[ObservableProperty] 是 MVVM Toolkit 最强大的特性之一。它自动为字段生成属性,并实现 INotifyPropertyChanged 通知。
传统方式(繁琐)
private string _name;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
使用 [ObservableProperty](简洁)
[ObservableProperty]
private string _name; // 自动生成 Name 属性,并实现属性变更通知
工作原理:
- 源生成器会自动生成 Name 属性
- 自动实现 PropertyChanged 事件通知
- 支持属性验证和自定义逻辑
3.2 [RelayCommand] 特性
[RelayCommand] 自动生成命令属性,用于处理按钮点击等操作。
传统方式(繁琐)
private ICommand _submitCommand;
public ICommand SubmitCommand
{
get
{
if (_submitCommand == null)
{
_submitCommand = new RelayCommand(ExecuteSubmit, CanExecuteSubmit);
}
return _submitCommand;
}
}
private void ExecuteSubmit()
{
// 执行逻辑
}
private bool CanExecuteSubmit()
{
return !string.IsNullOrEmpty(UserName);
}
使用 [RelayCommand](简洁)
[RelayCommand]
private void Submit() // 自动生成 SubmitCommand 属性
{
// 执行逻辑
}
[RelayCommand(CanExecute = nameof(CanSubmit))]
private void Submit()
{
// 执行逻辑
}
private bool CanSubmit() => !string.IsNullOrEmpty(UserName);
四、示例
4.1 Model(数据模型)
// Models/User.cs
namespace WPF_DEMO.Models
{
/// <summary>
/// 最简单的模型:只包含一个 Name 属性
/// </summary>
public class User
{
public string Name { get; set; } = string.Empty;
}
}
4.2 ViewModel(视图模型)
// ViewModels/MainWindowViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using WPF_DEMO.Models;
namespace WPF_DEMO.ViewModels
{
public partial class MainWindowViewModel : ObservableObject
{
// 一个模型:User(用 ObservableProperty 包起来)
[ObservableProperty]
private User _user = new();
// 一个属性:用于显示问候语
[ObservableProperty]
private string _greeting = "请输入姓名,然后点击按钮";
// 一个命令:点击后根据姓名生成问候语
[RelayCommand]
private void SayHello()
{
if (string.IsNullOrWhiteSpace(User.Name))
{
Greeting = "请先输入姓名";
}
else
{
Greeting = $"你好,{User.Name}!欢迎学习 MVVM。";
}
}
}
}
4.3 View(视图)
<!– Views/MainWindow.xaml –>
<Window x:Class="WPF_DEMO.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:WPF_DEMO.ViewModels"
mc:Ignorable="d"
Title="MVVM 最小示例" Height="200" Width="400">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!– 输入姓名 –>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
<TextBlock Text="姓名:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Width="200"
Text="{Binding User.Name, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<!– 按钮 –>
<Button Grid.Row="1"
Content="打招呼"
Width="80"
HorizontalAlignment="Left"
Command="{Binding SayHelloCommand}"/>
<!– 问候语显示 –>
<TextBlock Grid.Row="2"
Text="{Binding Greeting}"
FontSize="14"
Margin="0,10,0,0"
TextWrapping="Wrap"/>
</Grid>
</Window>
4.4 View 代码后置
// Views/MainWindow.xaml.cs
using System.Windows;
using WPF_DEMO.ViewModels;
namespace WPF_DEMO.Views
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
}
4.5 效果

五、小结
这一篇我们用一个「输入姓名 → 点击按钮 → 显示问候语」的极简示例,把 View / ViewModel / Model 拆开,并且实际演示了 ObservableProperty 和 RelayCommand 的使用方式。
后续我还会继续分享更完整的 MVVM 示例,比如列表、导航、依赖注入等场景,帮助你一步步把 WPF 项目从 Demo 搞成可以真正上线的应用。
这就是 WPF 系列的第二篇,如果你想直接看代码或获取后续更新,可以关注我的GitHub主页:https://github.com/leepinkz



