MVVM 模式入门笔记
MVVM 是 WPF 里最常见的组织代码的方式。它本身不是框架,而是一种分层约定,核心目的是让界面和业务逻辑分开,逻辑部分可以单独测试。
1. 三层职责
- Model:数据和业务规则,例如实体类、数据访问。它不关心界面。
- View:XAML 界面,负责显示。理想情况下它的后台代码文件(
.xaml.cs)里几乎没有逻辑。 - ViewModel:View 的数据来源,把 Model 的数据整理成界面需要的形式,并对外暴露属性和命令。它不引用任何界面控件类型。
判断 ViewModel 写得是否干净有个简单标准:里面如果出现了 MessageBox、TextBox、Window 这类界面类型,说明职责已经串了。
2. 用命令替代事件
WinForms 里按钮点击写在 Button_Click 事件里,MVVM 中改为绑定命令,逻辑放在 ViewModel。命令需要实现 ICommand,通常会写一个通用的 RelayCommand:
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
public void Execute(object parameter) => _execute();
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
ViewModel 中暴露命令属性:
public class LoginViewModel : ViewModelBase
{
public LoginViewModel()
{
LoginCommand = new RelayCommand(Login, () => !string.IsNullOrEmpty(UserName));
}
public ICommand LoginCommand { get; }
private void Login()
{
// 调用 Model 层完成登录逻辑
}
}
XAML 中绑定:
<Button Content="登录" Command="{Binding LoginCommand}" />
CanExecute 返回 false 时按钮会自动变灰,不需要手动控制 IsEnabled,这一点比手写事件省事很多。
3. 学习过程中踩到的几个点
- ViewModel 的属性一定要触发
PropertyChanged,否则界面不更新,这和数据绑定笔记里记的是同一个问题。 - 不要为了“纯粹的 MVVM”把简单界面也拆成三层,小工具窗口直接写在后台代码里反而更清楚。
- 弹窗、页面跳转这类操作不适合直接写在 ViewModel 里,一般会抽一个对话框服务接口,由 ViewModel 调接口、由界面层去实现。
- 正式项目里通常不会自己造
RelayCommand和ViewModelBase,社区已有成熟的实现,自己写一遍主要是为了理解原理。