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:
与视图具有一对一关系的视图模型(例如MainWindowViewModel
或CustomerEditorViewModel
)。假设只有一个MainWindow
,那么就只有一个MainWindowViewModel
。
与模型实例具有一对一关系的视图模型(例如CustomerViewModel
)并且是模型实例的某种“机甲套装”,提供额外的功能,例如计算属性(例如来自@的Duration
987654328@ 和 EndTime
)。一般公司有很多Customer
s,所以会有很多CustomerViewModel
s。
那么如何包装模型实例呢?
一个想法可能是创建派生自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/
希望对你有帮助。
【讨论】:
请非常友好地举一些Model
和ViewModel
的例子。
我想我会在博客上写下它,如果您仍然感兴趣,请随时通知您,这样会更容易解释整个事情。但这实际上并不复杂,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 吗?的主要内容,如果未能解决你的问题,请参考以下文章