WPF MVVM: INPC和视图模型与模型之间的中介通信

8
我已经阅读了各种关于将模型数据的更改通知给视图模型的方法。有些人建议模型在可能的情况下实现INotifyPropertyChanged,以便它可以通知视图模型已更改的属性。有些人建议在模型和视图模型之间增加服务层,服务层实现INPC,通过这个服务层将方法调用路由到模型,以便服务层通知视图模型。
我认为后者是前者的更细粒度的修订,并已开始在我的模型类中实现INPC。这感觉不对,因为:
a)我现在必须在我的视图模型中编写事件处理程序来处理来自模型的通知。这采用了一个长开关(propertyName)的形式,在其中设置相应的属性(s)在视图模型上,导致NPC再次向上发送。我觉得我在这里写了很多样板代码。
b)视图模型现在通过一堆仅由约定规定的字符串与我的模型耦合在一起。更不用说这给IDE带来的困难。
c)我的模型必须修改以适应这个上下文!如果由于某种原因关闭了它怎么办?我认为这样的模式旨在增加代码可重用性和关注点分离。不仅如此,但是触发INPC事件所需的代码是乏味的、重复的,而且不太抽象。
我非常想知道WPF专家们如何解决这个问题,无论是通过依赖属性等方式。我有一种感觉,我错过了什么。我不喜欢使用框架,因为我想从基础开始学习。我已经离开WPF一两年了,最近使用AngularJS工作让我质疑我的方法。
谢谢!

当你说“Model”时,你具体指的是什么?你是指业务对象/数据类型类,连接数据源的代码,还是两者都包括? - Sheridan
我指的是业务数据和功能。在这种情况下,我的模型类是“Test”(属性如“Description”,“Result”,方法如“Run”)和“TestPlan”,其中包括VMs“TestViewModel”和“TestPlanViewModel”。 - Drew R
你的模型不需要实现INPC,只需要在你的视图模型中实现。这就是视图模型的全部意义 - 我曾经看到有人在他们的模型中实现了INPC,但我认为这只是把它变成了一个视图模型,而不是一个模型。 - Ryan Amies
你的虚拟机中不必使用混乱的观察者(采用长switch(propertyName)的形式)。可以使用类似这个,从而避免一些麻烦。 - Viv
@Viv 我确实喜欢PropertyObserver解决方案,但您是否同意它只是一种解决方法呢?我很想知道微软WPF开发人员对此的意图。 - Drew R
显示剩余4条评论
1个回答

7
为了回答这个问题,我将把业务对象类称为“数据类型”。
就我的个人经验而言,视图模型始终与数据类型相关。您必须拥有要显示的类型的属性,因此视图模型命名空间中将始终引用数据类型命名空间。
从您在评论中的描述中所说的“模型”,听起来像是我的视图模型。我的视图模型具有属性,主要是各种数据类型类的类型和方法,而我的数据类型类只是大多数情况下保持属性... 它们只是数据的持有者和更改的报告者。
您似乎认为INotifyPropertyChanged接口在您所称的“模型”和视图模型类之间执行某些职责... 在我看来,这最多是可选的... 从MSDN的 INotifyPropertyChanged Interface页面上:
“INotifyPropertyChanged接口用于通知客户端(通常是绑定客户端)属性值已更改。”
因此,我认为INotifyPropertyChanged接口是在视图模型和数据对象之间流动的“生命线”。一些开发人员喜欢将每个数据类型都包装在自己的视图模型中,但我更喜欢直接在我的数据类型中实现INotifyPropertyChanged接口。
我这样做的主要原因是可以在我拥有的自定义集合类中钩入此框架。这使我能够拥有感知集合中任何项中任何属性更改的集合,还可以在我的基类中构建数据同步,以便对象知道它们是否有任何更改。
这也节省了创建匹配视图模型类的时间。为什么要用两个类来完成一个类可以完成的工作呢?我从未需要那种级别的分离。如果我理解你的意思正确,那么在数据类型类中实现此接口将消除执行点a)的需要。
一些你提到的 b) 和 c) 点可能会因为 .NET 4.5 的新 CallerMemberNameAttribute 属性而变得无效。你可以使用这个属性来自动将每个属性的名称传递给 PropertyChanged 处理程序。我找到了一篇好文章,名为 C# 5–Making INotifyPropertyChanged Easier,其中对此进行了良好的描述。
我现在已经编写了几个大型的 WPF 应用程序和一些框架,我从来没有在我的数据类型类中实现 INotifyPropertyChanged 接口遇到问题。事实上,如果我必须为每个数据类型类实现包装器视图模型类,我不认为我能够在同样的时间内编写它们。这种方法迄今为止一直为我服务良好,我计划继续使用它,至少在我找到更好的方法之前。然而,这只是一个开发者的观点,你需要选择适合你的方式。

哇,太棒了!感谢您抽出时间写下这个答案。 我的理解是,您的视图模型公开了模型类的实例,也就是数据类型本身是通知器,而不是视图模型逐个代理每个属性,我理解得对吗? 我对MVVM的理解是对屏幕上显示的数据进行建模。您可以根据每个模型类/数据类型进行建模,也可以根据每个VIEW进行建模,我认为后者与您的建议相符? - Drew R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接