MVVM 模式入门笔记

MVVM 是 WPF 里最常见的组织代码的方式。它本身不是框架,而是一种分层约定,核心目的是让界面和业务逻辑分开,逻辑部分可以单独测试。

1. 三层职责

  • Model:数据和业务规则,例如实体类、数据访问。它不关心界面。
  • View:XAML 界面,负责显示。理想情况下它的后台代码文件(.xaml.cs)里几乎没有逻辑。
  • ViewModel:View 的数据来源,把 Model 的数据整理成界面需要的形式,并对外暴露属性和命令。它不引用任何界面控件类型。

判断 ViewModel 写得是否干净有个简单标准:里面如果出现了 MessageBoxTextBoxWindow 这类界面类型,说明职责已经串了。

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 调接口、由界面层去实现。
  • 正式项目里通常不会自己造 RelayCommandViewModelBase,社区已有成熟的实现,自己写一遍主要是为了理解原理。

« 返回笔记列表