MVVM 中模型的职责是什么?

And*_*ens 5 wpf mvvm

我一直对“模型”应该或不应该包含什么感到有点困惑 - 教程和示例经常相互矛盾。到目前为止,我一直在谨慎行事,我的模型仅公开“UI 内容”,例如用于绑定到视图的属性以及验证逻辑。但是模型中包含其他业务逻辑是否可以接受?

假设我想通过网络服务控制机械泵,该服务提供了以给定速度打开泵以及再次关闭泵的方法。我的 UI 视图可能包括“开”和“关”按钮,以及一个用于设置速度的文本框。考虑到这一点,我的模型可能会这样开始:-

public class Pump
{
    public int Speed { get; set; }
}

// The real thing would implement INotifyPropertyChanged, validation, etc.
Run Code Online (Sandbox Code Playgroud)

这是模型应该做的全部事情吗?或者公开用于打开和关闭泵的业务逻辑是否可以接受 - 或者作为视图模型调用的方法,或者甚至作为可以直接绑定到视图的 ICommand纽扣?或者所有这些都应该在视图模型中?

编辑 不知道为什么投反对票,因为我认为这是一个完全合理的问题。虽然网络上有许多 MVVM 教程和示例,但它们经常提供关于什么去哪里的相互矛盾的建议,就像下面提供的答案一样。我什至最终阅读了 WPF 专家 Josh Smith 撰写的“高级 MVVM”电子书 - 这本书没有提到任何模型!

不管怎样,我在这里为任何想要了解 MVVM 结构的人找到了一个很好的资源。虽然该链接将带您进入 Microsoft Prism 框架的文档,但此特定页面全部是关于 MVVM 结构,很少或没有 Prism 特定内容。我发现该页面内容非常丰富,至少让我安心,确认我在过去几年中所做的事情是完全有效的 - 也就是说,拥有实现 INPC 和验证的模型 (IDataErrorInfo) ,并通过 VM 属性将这些模型直接绑定到视图(而不是像下面的一些答案所提倡的那样,在 VM 中重复模型属性)。

k.m*_*k.m 4

模型的目的是对您的领域进行建模。实体、业务逻辑、服务——这些都属于模型。将它与 UI 绑定的是视图模型。

在您的泵示例中,Pump不应实现该类INotifyPropertyChanged以便在 UI 中使用它,因为它不应该在 UI 中使用。这就是视图模型的作用。更合适的设计是

public class PumpService
{
   public void Start() { ... };
   public void Stop() { ... };
}

public class PumpControllerViewModel : INotifyPropertyChanged
{
    public PumpControllerViewModel(PumpService service)
    {
        StartPump = new Command(() => service.Start());
        // ...
    }
    public ICommand StartPump { get; private set; }
    public ICommand StopPump { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,模型对于 UI 来说几乎是不可见的。视图模型经常公开模型属性,但很少直接这样做。如果您的应用程序需要显示泵速视图模型则必须将其公开为可观察属性


更新回应OP问题

(...) 如果我有一个具有许多属性的更复杂的模型(例如客户)怎么办?(...)您建议通过虚拟机上的可观察属性公开每个模型属性 - 这不会涉及大量重复的属性、映射代码等吗?

模型复杂性在这里无关紧要,是的,这种方法涉及一些代码重复(尽管在某种程度上)。

如果您保持模型简单(DTO / POCO -simple),那么您需要做的就是将POCO -model重写为INPC -view model。模型类将具有很少的代码,而 VM 将处理视图通知(就其角色而言)。

假设最复杂的模型是通过工厂方法/构建器/服务创建的(如,不是直接创建),视图模型也可能是这样。然后您可以利用自动映射器、序列化器或任何您需要的工具来简化复制过程。

您将必须维护两个相似的类,但对于 MVVM 提供的干净分离来说,这是值得权衡的。