MVVM的基本概念-ViewModel应该做什么?

99

尝试理解 MVVM 的概念,我已经阅读了几篇博客并查看了一些项目。

据我所知,View 是一个“哑巴”,它只知道如何呈现传递给它的东西。

Models 只是普通的数据,而 ViewModel 是两者之间的一个缓冲层,应该从 Model 获取信息并将其传递给 View,而 View 应该知道如何呈现它。或者反过来,如果 View 中的信息发生变化,它应该将更改传递给 Model

但我仍然不知道如何应用这个概念。有人能够解释一下非常简单的场景,以便我能够理解吗?我已经看过几个项目,但仍然不完全明白,如果有人能用简单的英语写出来,那就太好了。


3
不同意“视图只知道如何呈现传递给它的内容”这个说法。视图从VM中获取数据,没有人将数据传递给它,也没有人知道视图的存在。这与MVP模式不同,可以把它看作是一个带有代码后台逻辑的简单WPF应用程序。在MVP模式中,代码后台是一个Presenter,会像你所说的那样将数据传递给View。 - Grigory
4个回答

189

我喜欢这样理解:

视图(views)本身是无能为力的。Josh Smith 是MVVM模式的重要作者之一,他在经典的MSDN文章中说过:视图只是数据“穿着”的衣服。视图从不实际包含数据或直接操作它们,它们只是绑定到你的视图模型(viewmodels)的属性和命令。

模型(models)是模拟你应用程序领域对象的对象,例如业务对象。如果你的应用程序是音乐商店,则你的模型对象可能是艺术家、专辑和歌曲。如果你的应用程序是一个组织结构图浏览器,则你的模型对象可能是经理和员工。这些模型对象与任何形式的可视化呈现都没有关系,它们甚至与你放置它们的应用程序也没有直接关联——你的模型对象应该完全独立地代表某种领域的对象系列。模型层还通常包括服务访问器等东西。

这就带我们来到了视图模型(viewmodels)。什么是视图模型?它们是模拟图形用户界面(GUI)应用程序的对象,即它们提供要由视图使用的数据和功能。它们是定义你正在构建的实际应用程序的结构和行为的对象。对于模型对象,领域是你选择的任何领域(音乐商店、组织结构图浏览器等),但对于视图模型而言,领域是一个图形应用程序。你的视图模型将封装应用程序执行的所有行为和数据。它们将公开对象和列表作为属性,以及诸如命令之类的东西。命令只是一种行为(通常是一次方法调用)被包装成携带它的对象,这个想法很重要,因为视图是由数据绑定驱动的,这些数据绑定将可视化控件与对象关联起来。在MVVM中,你不会为按钮提供一个点击处理程序方法,而是将它绑定到一个命令对象(从视图模型的属性中提供)上,该对象包含了你单击时想要运行的功能。

对我而言,最困惑的部分是以下内容:

  • 尽管viewmodels是图形应用程序的模型,它们并不直接引用或使用视觉概念。例如,您不希望在ViewModels中引用Windows控件 - 这些东西应该放在view中。ViewModels只是向将绑定到它们的控件或其他对象公开数据和行为。例如 - 您有一个包含ListBox的视图吗?您的ViewModel几乎肯定会在其中有某种集合。你的视图有按钮吗?你的ViewModel几乎肯定会有一些命令。
  • 有几种对象可以被认为是“ViewModels”。最简单的ViewModel类型是直接表示控件或屏幕的1:1关系的ViewModel,例如,“屏幕XYZ具有一个文本框、一个列表框和三个按钮,因此ViewModel需要一个字符串、一个集合和三个命令”。另一种适合于ViewModel层的对象是一个包装器,它在模型对象周围提供行为并使其更易于被视图使用 - 这就涉及到“厚”和“薄”ViewModel层的概念。 “薄”ViewModel层是一组ViewModel,直接将您的模型对象暴露给视图,这意味着视图最终会直接绑定到模型对象的属性。这对于像简单的只读视图之类的东西可能起作用,但是如果您想要与每个对象相关联的行为怎么办?您不希望在模型中进行此操作,因为模型与应用程序无关,它只与您的领域相关。您可以将其放入包装模型对象并提供更易于绑定的数据和行为的对象中。这个包装对象也被认为是ViewModel,并且拥有它们将导致“厚”ViewModel层,在其中您的视图永远不会直接绑定到模型类上的任何内容。集合将包含包装模型的ViewModel而不仅仅是包含模型本身。
  • 兔子洞口更深 - 还有许多习语需要理解,如ValueConverters,它们保持MVVM的工作正常,当您开始思考Blendability、测试以及如何在应用程序中传递数据并确保每个viewmodel都可以访问所需的行为时,需要应用很多知识(这就是依赖注入的作用),但希望以上内容是一个良好的起点。关键是将您的视觉效果、领域和实际应用程序的结构和行为视为三种不同的事物。


6
+1 - 我因为在一份示例代码中遇到了一个“包装器” ViewModel 而感到困惑,结果来到了这里。谢谢你帮我澄清了这个问题 :-) - Riegardt Steyn
1
非常好的答案——希望我能给它加10分。 - Nick Hodges
1
@nlawalker 非常棒!你上面评论的双位元真让我困惑了很长时间。许多文章和博客只是告诉我们MVVM的“关键特性”,但当你尝试使用它时,事情变得非常复杂!比如,“如何导航这些视图?”“在设计ViewModel时应该选择什么样的适当粒度?”“一个视图必须有一个匹配的ViewModel,还是一个ViewModel可以在不同的视图中重用?”你对由“Slim VM”和“Thick VM”组成的ViewModel进行澄清确实很有道理!我正在尝试,目前效果很好!:) - Claw
@nlawalker谢谢!还有一个问题:我有一个TreeView并创建了TreeViewViewModel。如果我在我的ViewModel中创建像:ExpandTree()这样的方法,这是正确的方式吗? - user2545071
这是一个精彩的解释。谢谢! - KunYu Tsai
显示剩余2条评论

31

使用这篇非常有帮助的文章作为来源,下面是关于ViewViewModelModel的摘要。


View:

  • 视图是一种可视元素,例如窗口、页面、用户控件或数据模板。视图定义了视图中包含的控件及其视觉布局和样式。

  • 视图通过其DataContext属性引用视图模型。视图中的控件通过视图模型公开的属性和命令进行数据绑定。

  • 视图可以自定义视图与视图模型之间的数据绑定行为。例如,视图可以使用值转换器对在 UI 中显示的数据进行格式化,或者使用验证规则向用户提供额外的输入数据验证。

  • 视图定义并处理 UI 的视觉行为,例如状态更改或用户与 UI 的交互可能触发的动画或转换。

  • 视图的代码后台可以定义 UI 逻辑来实现在 XAML 中难以表达或需要直接引用视图中定义的特定 UI 控件的视觉行为。

注意:
因为视图模型不应该对视图中特定的可视元素具有明确的知识,所以编写代码来编程性地操作视图内的可视元素应该驻留在视图的代码后台或者封装在行为中。


View Model:

  • 视图模型是一个非可视类,不派生自任何 WPF 或 Silverlight 基类。它封装了支持应用程序中使用情况或用户任务所需的呈现逻辑。视图模型可独立于视图和模型进行测试。

  • 视图模型通常不会直接引用视图。它实现属性和命令,供视图进行数据绑定。它通过INotifyPropertyChangedINotifyCollectionChanged接口的更改通知事件来通知视图状态的任何更改。

  • 视图模型协调视图与模型的交互。它可能转换或操作数据,以便视图可以轻松消耗,并可以实现在模型上不存在的其他属性。它还可以通过IDataErrorInfoINotifyDataErrorInfo接口实现数据验证。

  • 视图模型可以定义逻辑状态,供视图向用户表示。

注意:
任何对应用程序的逻辑行为重要的内容应该放在视图模型中。检索或操作通过数据绑定显示在视图中的数据项的代码应该驻留在视图模型中。


模型:

  • 模型类是非可视化类,封装了应用程序的数据和业务逻辑。它们负责管理应用程序的数据,并通过封装所需的业务规则和数据验证逻辑来确保其一致性和有效性。

  • 模型类不直接引用视图或视图模型类,也没有依赖于它们的实现方式。

  • 模型类通常通过INotifyPropertyChangedINotifyCollectionChanged接口提供属性和集合更改通知事件。这使它们可以很容易地在视图中进行数据绑定。表示对象集合的模型类通常派生自ObservableCollection<T>类。

  • 模型类通常通过IDataErrorInfoINotifyDataErrorInfo接口提供数据验证和错误报告。

  • 模型类通常与封装数据访问和缓存的服务或存储库一起使用。


  • System.Windows.Data 中的内容应该在 View 还是 ViewModel 中使用?它不包含视觉元素,但它在 PresentationFramework.dll 程序集中,这意味着它是特定于 WPF 的。我主要关注过滤逻辑。 - Piotr Golacki

    22

    我尽可能用“通俗易懂”的语言在这个MVVM系列中进行了阐述。 特别是,这个图示可能是最简单、最短的解释。

    话虽如此,基本上,“模型”就是您的数据或业务规则。它真的不应该知道它将在哪里被使用,特别是不应该知道将使用哪种技术。 “模型”是应用程序的核心部分——它不需要担心应用程序是WPF、Silverlight、Windows Forms、ASP.NET等——它只是以纯形式存在。

    “视图”是完全针对技术的部分。在MVVM中,理想情况下,视图应该近乎100%是XAML,因为这提供了一些巨大的灵活性。

    然而,需要有些东西将信息从模型转换为某种形式,以便可以由手头的技术使用——这就是ViewModel的作用所在。例如,这经常会将模型类“包装”成一个“ViewModel”,以包括命令(用于运行逻辑),实现INotifyPropertyChanged(用于数据绑定支持)等。这就是它的作用——它是使模型能够为视图所用的桥梁。


    好的,谢谢。我曾将Model视为对象,而将ViewModel视为处理这些对象的方法,以便视图能够理解Model“对象”。但是有人告诉我这是错误的,因为ViewModel本身也是对象。我想这就是真正让我感到困惑的地方。 - RKM
    @Rosie:我建议你阅读(或至少浏览)我引用的系列文章。我写这个系列是因为几乎没有关于MVVM的文章不假设你已经理解了MVC或MVP等。这真的是一个“一步一步”的过渡 ;) - Reed Copsey
    意识到这是僵尸线程的一点点......您的图表显示VM包含“应用程序特定状态和逻辑”(我的强调)。这是否意味着您认为VM可以/应该包含数据QA逻辑?相关的RssWpfMVVM.csproj似乎在其两个视图模型中没有明显的QA。 - ruffin
    2016年的图表:https://web.archive.org/web/20161014093417/http://reedcopsey.com/blog/wp-content/uploads/2010/01/MVVM.png 以及该系列文章:https://web.archive.org/web/20110305162551/http://reedcopsey.com/series/windows-forms-to-mvvm/ - Sabuncu

    2
    一个很好的MVVM入门视频可以在Jason Dolinger的这里找到。当我刚开始学习时,我一直保存着这个视频,它非常有用。

    1
    链接已失效。 - ibrahim mahrir
    1
    @ibrahimmahrir ~ 我已更新到一个可用的链接。我正在下载视频。 - InteXX

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