视图模型

视图模型是 MV VM 中VM 。这是一个充当中间人的类,将模型公开给用户界面(视图),并处理来自视图的请求,例如按钮点击引发的命令。这是一个基本的视图模型:

public class CustomerEditViewModel
{
    /// <summary>
    /// The customer to edit.
    /// </summary>
    public Customer CustomerToEdit { get; set; }

    /// <summary>
    /// The "apply changes" command
    /// </summary>
    public ICommand ApplyChangesCommand { get; private set; }

    /// <summary>
    /// Constructor
    /// </summary>
    public CustomerEditViewModel()
    {
        CustomerToEdit = new Customer
                         {
                             Forename = "John",
                             Surname = "Smith"
                         };

        ApplyChangesCommand = new RelayCommand(
            o => ExecuteApplyChangesCommand(), 
            o => CustomerToEdit.IsValid);
    }

    /// <summary>
    /// Executes the "apply changes" command.
    /// </summary>
    private void ExecuteApplyChangesCommand()
    {
        // E.g. save your customer to database
    }
}

构造函数创建一个 Customer 模型对象并将其分配给 CustomerToEdit 属性,以便它对视图可见。

构造函数还创建一个 RelayCommand 对象并将其分配给 ApplyChangesCommand 属性,再次使其对视图可见。WPF 命令用于处理来自视图的请求,例如按钮或菜单项单击。

RelayCommand 有两个参数 - 第一个是在执行命令时被调用的委托(例如,响应按钮点击)。第二个参数是一个委托,它返回一个布尔值,指示命令是否可以执行; 在这个例子中,它连接到客户对象的 IsValid 属性。如果返回 false,则会禁用绑定到此命令的按钮或菜单项(其他控件的行为可能不同)。这是一个简单但有效的功能,无需编写代码来启用或禁用基于不同条件的控件。

如果你确实启动并运行了此示例,请尝试清空其中一个 TextBoxes(将 Customer 模型置于无效状态)。当你远离 TextBox 时,你会发现应用按钮被禁用。

关于客户创造的评论

视图模型不实现 INotifyPropertyChanged(INPC)。这意味着如果要将一个不同的 Customer 对象分配给 CustomerToEdit 属性,那么视图的控件将不会更改以反映新对象 - TextBoxes 仍将包含前一个客户的名字和姓氏。

示例代码的工作原理是因为 Customer 是在视图模型的构造函数中创建的,之后它被分配给视图的 DataContext(此时绑定被连接起来)。在实际应用程序中,你可能正在使用构造函数以外的方法从数据库中检索客户。为了支持这一点,VM 应该实现 INPC,并且应该更改 CustomerToEdit 属性以使用你在示例 Model 代码中看到的扩展getter 和 setter 模式,从而在 setter 中引发 PropertyChanged 事件。

视图模型的 ApplyChangesCommand 不需要实现 INPC,因为命令不太可能改变。你需要,如果你创建了比其他构造的命令的地方,例如某种 Initialize() 方法来实现这种模式。

一般规则是:如果属性绑定到任何视图控件并且属性的值能够在构造函数之外的任何位置更改,则实现 INPC。如果仅在构造函数中分配属性值,则不需要实现 INPC(并且你将在过程中节省一些输入)。