MVVM Light 中有两种 ViewModel 吗?

Are there two kinds of ViewModels in MVVM Light?

许多人建议 WPF MVVM 开发人员不要将 ViewModel 中的 Model 实例暴露给 View。要显示来自 Model 实例集合的信息,请将所有单个项目包装到 ViewModel 实例中,并将 ViewModel 集合公开给 View。

然而,使用 MVVM Light 在我看来有两种 ViewModel:

然后如何包装模型实例?

一个想法可能是创建从 ViewModelBase 派生的包装器 classes 但不使用 ViewModelLocator 注册和实例化那些包装器。我认为将两个单独的东西都称为 ViewModel 不是一个好主意。

另一个想法可能是为第二种类型的 ViewModel 使用新的基础 class,可能称为 ModelInfo。在 MainViewModel 的单个实例中,会有 CustomerInfo 个实例的集合,为 Customer 模型数据提供附加功能。

我倾向于后者,但由于这似乎是使用 MVVM Light 的一个非常普遍的情况,我相信这个问题一定有一个通用的解决方案。


更新

我找到了MVVM Light的作者an article by Laurent Bugnion。 在他 2012 年的文章中,Bugnion 使用两种不同的初始化 ViewModel 的方法:

  1. MainViewModel 注册到 ViewModelLocator 并且没有构造函数参数。因此适合Dependency Injection,可以通过ServiceLocator实例化。
  2. FriendViewModel 未在 ViewModelLocator 中注册,其构造函数将模型实例作为参数。它不能用ServiceLocator实例化,只能通过直接调用构造函数并传递模型实例来实例化。

这与我在最初的问题中提到的区别非常一致,并且与第一个想法如何包装模型实例一致。

这里是我个人 7 年的 MVVM 经验。我说个人是因为你会发现这个话题有很多矛盾,尤其是当你参考官方的时候MSDN definition

"it seems to me there are two kinds of ViewModels"

绝对不是,但这是一个普遍的误解。 ViewModel 的第一个角色是成为 View 的可测试和可维护的表示。正确的抽象是与您的视图的一对一关系,而不是您的模型。

"Many people advice WPF MVVM developers to not expose Model instances from the ViewModel to the View."

是的,它仍然是正确的,因为如果从OOP的角度正确实现了你的模型,你就把职责和业务逻辑放在里面了。因此,您的 ViewModel 只是 INotifiedPropertyChanged,并包装您想要公开的信息 来自 您的模型,以及您想要调用的命令 你的模型中。

经典解释(包括 MSDN 的解释)是不完整的,因为它假定您可以将一个视图与一个视图模型与一个模型对齐。因此你的问题经常被忽视,因为在一个非常 CRUD 的系统中,你可以很容易地拥有这种一对一的关系。另一种建立这种关系的方法是使用CQRS,因为它允许你生成一个没有任何逻辑的模型,并直接在你的视图上对齐以供查询。

但是正如您已经体验过的(如果您不在 CRUD 或 CQRS 系统上工作),在大多数经典实现中,您的 ViewModel 代表您的视图,但需要多个模型才能正常工作(这是很自然的)。您必须在这些模型中放置尽可能多的业务逻辑。为了管理对不同模型的调用之间的流程,您添加了另一个抽象,可以称为服务。该服务应该代表一个业务案例,需要使用多个模型。

您可以这样想:您的 BusinessService 应该独立于基础架构工作。它不应该关心它是从 ViewModel 调用,还是从 Web 应用程序中的控制器调用。它只是管理一些模型之间的流程以满足业务需求。

让我试着回顾一下:

  • ViewModel 不应该有业务逻辑(但你已经明白了)

  • ViewModel 是您的视图的抽象(而不是您的模型,即使在某些情况下您可以获得一对一的关系)

  • 如果一个 ViewModel 需要多个模型来满足业务需求,请使用一个服务来管理不同模型之间的流(根据定义,它成为一个 BusinessService)

这是一篇博客 post,其中的代码示例阐明了我的观点: http://ouarzy.azurewebsites.net/2016/04/14/clarifying-mvvm-with-ddd/

希望对您有所帮助。

据我所知,如上所述,ViewModel 与 View 具有一对一的关系。

在实践中,我发现我在没有视图的情况下使用 ViewModel——通常是在使用 ItemsSource 显示它们的集合时。如果需要它,我将有一个 ItemsSource 视图 - 但如果它非常简单,我将只使用 "parent" 视图。不确定这是否是最佳实践,但是有一点你需要绑定到模型的普通属性以外的东西,并且需要绑定到视图相关的属性,但是专用视图没有意义(在 DataGrid 行中例如,Row 可能是 ViewModel,但没有 View)。

这是我所说的一个例子。一个随机示例应用程序具有三个 "Main" 个 ViewModel 和视图:

更详细地说,这是它们的结构。请注意 "EmployeeViewModel" 没有 "EmployeeView"。类似地,这些 "Screen" 视图模型根本没有任何模型,它们并没有真正与数据对象相关联,也不需要模型:

如果我们只绑定到直接的数据项(并且没有其他与视图相关的逻辑,比如 "Hidden" 属性 到 show/hide 我们的员工)那么我们就不会不需要 EmployeeViewModel,我们可以将我们的 EmployeeTrackerScreenViewModel 绑定到许多 EmployeeModel。