MVVM Light 中有两种 ViewModel 吗?

Posted

技术标签:

【中文标题】MVVM Light 中有两种 ViewModel 吗?【英文标题】:Are there two kinds of ViewModels in MVVM Light? 【发布时间】:2016-07-25 02:24:16 【问题描述】:

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

但是,在我看来,使用 MVVM Light 有两种 ViewModel:

与视图具有一对一关系的视图模型(例如MainWindowViewModelCustomerEditorViewModel)。假设只有一个MainWindow,那么就只有一个MainWindowViewModel。 与模型实例具有一对一关系的视图模型(例如CustomerViewModel)并且是模型实例的某种“机甲套装”,提供额外的功能,例如计算属性(例如来自@的Duration 987654328@ 和 EndTime)。一般公司有很多Customers,所以会有很多CustomerViewModels。

那么如何包装模型实例呢?

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

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

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


更新

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

    MainViewModel 注册到 ViewModelLocator 并且没有构造函数参数。因此它适用于依赖注入,可以通过ServiceLocator实例化。 FriendViewModel 未在 ViewModelLocator 中注册,其构造函数将 Model 实例作为参数。不能用ServiceLocator实例化,只能直接调用构造函数,传递模型实例。

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

【问题讨论】:

我不明白您为什么需要“CustomerViewModel”。这对我来说就像一个模型类。我通常在我的 VM 中有一个类似于“SelectedCustomer”的属性,它可以将信息从模型类传递到模型类。 【参考方案1】:

这是我个人 7 年使用 MVVM 的经验。我说个人,因为你会在这个话题上发现很多矛盾,特别是当你参考官方MSDN definition

“在我看来有两种 ViewModel”

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

“许多人建议 WPF MVVM 开发人员不要公开模型实例 从 ViewModel 到 View。”

是的,它仍然是正确的,因为如果从OOP 的角度正确实现了您的模型,那么您将责任和业务逻辑放入其中。因此,您的 ViewModel 只是 INotifiedPropertyChanged,并包装了您想要从您的模型 公开的信息,以及您想要在您的模型中调用 in 的命令.

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

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

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

让我回顾一下:

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

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

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

这是一篇带有代码示例的博客文章,以阐明我的观点: http://ouarzy.azurewebsites.net/2016/04/14/clarifying-mvvm-with-ddd/

希望对你有帮助。

【讨论】:

请非常友好地举一些ModelViewModel的例子。 我想我会在博客上写下它,如果您仍然感兴趣,请随时通知您,这样会更容易解释整个事情。但这实际上并不复杂,ViewModel 必须在视图的属性更改时通知,仅此而已。模型应该为客户管理诸如“UpdateAdress”之类的业务案例。 @Quarzy 非常感谢您提供的信息丰富的回答。根据您的信息,我再次研究了在 MVVM Light 中实例化 ViewModel 的主题并找到了一些东西(请参阅我的编辑)。我还没有完全理解商业服务的概念。我接受了你的回答,因为它让我继续我的项目,但如果你有时间,如果你能更详细地指定 的含义,我真的很感激,并管理对不同模型的调用之间的流程,您添加另一个抽象,可以称为服务” 我发现了MVVM Light作者的一篇有趣的文章。它基本上表明您实际上可以区分不同种类的 ViewModel,至少它们的实例化方式不同,并且至少在他的框架的上下文中(我正在使用)。请参阅我上面的编辑。我还是同意你的回答;正如你自己所说,这个话题总体上存在很多矛盾。 如承诺的那样,这里是博文:ouarzy.azurewebsites.net/2016/04/14/clarifying-mvvm-with-ddd【参考方案2】:

据我所知,ViewModel 与 View 是一对一的关系。

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

这是我所说的一个例子。一个随机示例应用程序具有三个“主要”视图模型和视图:

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

如果我们只绑定到直接的数据项(并且没有其他与视图相关的逻辑,比如说一个“隐藏”属性来显示/隐藏我们的员工),那么我们就不需要 EmployeeViewModel,我们可以只绑定我们的EmployeeTrackerScreenViewModel 到许多 EmployeeModels。

【讨论】:

感谢您的回答。这是将 MVVM 应用于现实世界示例的罕见示例之一,显示了将 ViewModel 与 View 关联起来的困难 我也在为同样的事情苦苦挣扎。一旦你超越了超级简单的例子,一切都变得非常不确定。这是基于我的问题:programmers.stackexchange.com/questions/309449/… 我试图在这里展示更复杂的例子来澄清我的解释:ouarzy.azurewebsites.net/2016/04/14/clarifying-mvvm-with-ddd

以上是关于MVVM Light 中有两种 ViewModel 吗?的主要内容,如果未能解决你的问题,请参考以下文章

MVVM模式用依赖注入的方式配置ViewModel并注册消息

MVVM Light须要注意的10个问题

MVVM Light须要注意的10个问题

Mvvm Light Toolkit 入门

MVVM-light 中的清理与处置(布尔)

MVVM Light新手初识MVVM,你一看就会