模型视图视图模型是由Microsoft开发的,目标是支持事件驱动编程的UI开发平台,特别是使用XAML和.NET语言的.NET平台上的Windows Presentation Foundation(WPF)和Silverlight。此后的几年中,许多Javascript框架(例如Angular,Knockout和ExtJS)都采用了这种模式。
像大多数软件模式一样,MVVM具有适当的用途和滥用。在什么条件下使用MVVM是合适的?什么时候不明智?
模型视图视图模型是由Microsoft开发的,目标是支持事件驱动编程的UI开发平台,特别是使用XAML和.NET语言的.NET平台上的Windows Presentation Foundation(WPF)和Silverlight。此后的几年中,许多Javascript框架(例如Angular,Knockout和ExtJS)都采用了这种模式。
像大多数软件模式一样,MVVM具有适当的用途和滥用。在什么条件下使用MVVM是合适的?什么时候不明智?
Answers:
MVVM旨在用于需要使用高保真的UI进行复杂的用户交互(即WPF)的地方。
MVVM针对的是现代UI开发平台(Windows Presentation Foundation或WPF和Silverlight),在该平台中,用户体验(UXi)开发人员的要求与更“传统”开发人员的要求不同(例如,面向业务逻辑和后端开发)。MVVM的View-Model是“基本上是类固醇的值转换器”,这意味着View-Model负责以易于管理和使用的方式公开来自Model的数据对象。在这方面,视图模型比视图更多,并且处理大多数(如果不是全部)视图显示逻辑。
MVVM旨在利用WPF中的特定功能,通过从View层中删除几乎所有“隐藏代码”来更好地促进View层开发与其余模式的分离。他们可以使用本机WPF标记语言XAML并创建与由应用程序开发人员编写和维护的ViewModel的绑定,而不必要求交互设计器编写View代码。角色的这种分离使Interactive Designers可以专注于UX需求,而不是编程或业务逻辑,从而允许在多个工作流中开发应用程序的各个层。
对于不需要这种丰富交互的UI,MVVM可能会过大。MVP可能是更自然的选择。对于Web应用程序,MVC更合适。对于永远不会增长的很小的应用程序(例如小的Winforms实用程序),代码隐藏就足够了。
MVVM是设计不良的数据绑定层的创可贴。特别是,由于WPF / XAML中数据绑定的局限性,它在WPF / silverlight / WP7世界中已获得大量使用。
从现在开始,我将假设我们正在谈论WPF / XAML,因为这将使事情变得更清楚。让我们看一下MVVM在WPF / XAML中要解决的一些缺点。
数据形状与UI形状
MVVM中的“ VM”创建了一组用C#定义的对象,这些对象映射到XAML中定义的一组表示对象。这些C#对象通常通过表示对象上的DataContext属性连接到XAML。
因此,viewmodel对象图需要映射到应用程序的表示对象图上。这并不是说映射必须是一对一的,但是如果窗口控件包含列表控件,则必须有一种方法可以从窗口的DataContext对象获取描述该列表数据的对象。
viewmodel对象图成功地将模型对象图与ui对象图解耦,但是以必须构建和维护的附加viewmodel层为代价。
如果我想将某些数据从屏幕A移到屏幕B,则需要弄乱视图模型。在业务人员看来,这是UI更改。它应该纯粹在XAML世界中进行。可悲的是,它很少能做到。更糟糕的是,取决于视图模型的结构方式和数据更改的积极程度,可能需要大量的数据重新路由才能完成此更改。
解决非表达性数据绑定
WPF / XAML绑定的表达不足。基本上,您将提供一种到达对象的方法,一种遍历的属性路径以及绑定转换器以使数据属性的值适应表示对象的要求。
如果您需要将C#中的属性绑定到比这更复杂的内容上,那么您基本上就不走运了。我从来没有见过没有绑定转换器的WPF应用程序,该转换器将true / false转换为Visible / Collapsed。许多WPF应用程序还趋向于具有称为NegatingVisibilityConverter或类似的东西,可以颠倒极性。这应该引起警钟响起。
MVVM为您提供了用于构造C#代码的准则,这些准则可用于克服此限制。您可以在视图模型上公开一个名为SomeButtonVisibility的属性,并将其绑定到该按钮的可见性。您的XAML既漂亮又漂亮……但是您已经成为一名职员了-现在,当UI演变时,必须在两个位置(UI和C#中的代码)公开+更新绑定。如果需要在另一个屏幕上使用相同的按钮,则必须在该屏幕可以访问的视图模型上公开一个类似的属性。更糟糕的是,我不能只看XAML看看按钮何时将可见。一旦绑定变得不平凡,我就必须在C#代码中进行侦探工作。
对数据的访问范围很大
由于数据通常是通过DataContext属性进入UI的,因此很难在整个应用程序中一致地表示全局或会话数据。
“当前登录用户”的想法就是一个很好的例子-在您的应用程序实例中,这通常是一件真正的全局性事情。在WPF / XAML中,很难以一致的方式确保对当前用户的全局访问。
我想做的是在数据绑定中自由使用“ CurrentUser”一词来指代当前登录的用户。相反,我必须确保每个 DataContext都给我一种获取当前用户对象的方式。MVVM可以适应这种情况,但是视图模型将变得一团糟,因为它们都必须提供对全局数据的访问。
MVVM崩溃的示例
假设我们有一个用户列表。我们希望在每个用户旁边显示一个“删除用户”按钮,但前提是当前登录的用户是管理员。同样,不允许用户删除自己。
您的模型对象不应该知道当前登录的用户,它们只会代表数据库中的用户记录,但是某种程度上,当前登录的用户需要暴露给列表行内的数据绑定。MVVM指示我们应该为组成当前登录用户和该列表行所代表的用户的每个列表行创建一个viewmodel对象,然后在该viewmodel对象上公开一个名为“ DeleteButtonVisibility”或“ CanDelete”的属性(取决于您的感受)关于绑定转换器)。
在大多数其他方式上,该对象看起来将非常类似于User对象-它可能需要反映用户模型对象的所有属性,并在数据更改时将更新转发给用户。感觉真的很棘手-再次,MVVM通过迫使您维护此类似于用户的对象使您成为店员。
考虑-您可能还必须在数据库,模型和视图中表示用户的属性。如果您和数据库之间有一个API,那就更糟了-它们在数据库,API服务器,API客户端,模型和视图中表示。我真的很犹豫采用一种设计模式,该模式添加了每次添加或更改属性时都需要触摸的另一层。
更糟糕的是,此层随UI的复杂性而扩展,而不随数据模型的复杂性而扩展。通常,相同的数据会在许多地方和用户界面中表示-这不仅会增加一层,还会增加具有很多额外表面积的层!
事情可能如何
在上述情况下,我想说:
<Button Visibility="{CurrentUser.IsAdmin && CurrentUser.Id != Id}" ... />
CurrentUser将在全球范围内向我的应用程序中的所有XAML公开。ID将引用DataContext上的属性作为我的列表行。可见性会自动从布尔值转换。对Id,CurrentUser.IsAdmin,CurrentUser或CurrentUser.Id的任何更新都会触发此按钮可见性的更新。十分简单。
相反,WPF / XAML强制其用户创建一个完整的混乱局面。据我所知,一些富有创意的博客在这个烂摊子上拍了一个名字,那个名字是MVVM。不要上当了-它与GoF设计模式不在同一个类中。这是一个丑陋的技巧,可以解决丑陋的数据绑定系统。
(如果您需要进一步阅读,这种方法有时称为“功能性反应式编程”)。
结论
如果必须使用WPF / XAML,我仍然不建议使用MVVM。
您希望代码的结构像上面的“本来该如何做”的示例那样-通过复杂的数据绑定表达式+灵活的值强制将模型直接暴露给视图。它的方式更好-更具可读性,可写性和可维护性。
MVVM告诉您以更冗长,更难以维护的方式来构造代码。
代替MVVM,构建一些东西来帮助您近似获得良好的体验:开发一个约定,以将全局状态一致地公开到您的UI。利用绑定转换器,MultiBinding等构建一些工具,使您能够表达更复杂的绑定表达式。建立自己的绑定转换器库,以帮助减轻常见的强制案例的痛苦。
甚至更好-用更具表现力的东西代替XAML。XAML是用于实例化C#对象的非常简单的XML格式-提出一个更具表现力的变体并不难。
我的另一项建议:不要使用强制执行此类妥协的工具包。通过将您推向像MVVM这样的废话,而不是专注于问题领域,它们将损害最终产品的质量。
我非常喜欢MVVM,发现它激发了许多挑战,并看到了很多好处,但是...
对于需要大量UI /交互代码来添加许多自定义行为并同时保持性能的应用程序或游戏-最好使用有点脏的MVVM-在有用或以数据为中心的情况下使用它代码的以交互为中心的区域。假设您要创建一个控件并将其在不同的父控件之间移动,或者以其他方式对其进行缓存...
它的学习曲线非常陡峭,因此,除非您有时间,计划在WPF / SL中进行大量开发,让Blender的设计人员熟练,觉得有必要为UI编写测试代码或希望为您进行数年的维护专案-可能无法获得回报。
没有多少设计人员知道Blend,并不是所有项目都值得将自动测试放在表示层上,因为无论如何都需要对其进行手动测试,这就是您将发现最重要的bug的方式,而不是通过测试VM来实现的。
这确实是一项投资。首先,您必须了解WPF / SL和XAML的基础知识,然后找出正确进行绑定的方法,以某种顺序连接与vm,正确掌握命令,在大多数情况下选择一个框架会因许可而出现问题,构建一个代码段库以高效地进行编码,只是发现该平台并不总是可以与绑定很好地工作,并且需要建立一个行为库来满足您的需求。
总体而言-如果您克服了所有障碍并相当熟练地使用该模式-所有这些都可以得到清晰度,可维护性和……吹牛的权利?:)
多年来,我一直是WPF / Silverlight程序员,在MVVM上构建大型应用程序(例如交易系统)。
对我来说,随着岁月的流逝,我了解到严格的MVVM会浪费时间和金钱。严格来说,我指的是诸如“无代码遗忘”之类的规则。
除了最基本的表单/数据库应用程序之外,没有任何代码隐藏是不可能的。
您的设计师将在第一天指定标准控件无法实现的功能,因此在使用MVVM时,您必须构建一系列自定义控件或包装现有控件以支持交互范例。
我已经使用数学,惯性,手势等及其艰苦的工作编写了各种各样的swish UI控件。
但这全都隐藏在代码中,只是不在视图中。您必须处理按钮的单击,滚动,拖动等操作,但是所有这些操作都很好地包装在一组控件中,这在某种程度上使此代码成为可能。
只需在视图中编写代码隐藏和巧妙的UI内容,而不是仅仅为了它而建立一组控件,通常会更容易。
MVVM的目标是将应用程序/业务逻辑与UI逻辑分开。绑定系统和MVVM是执行此操作的好方法。
吹捧的其他目标(例如,一个工作台上的XAML设计器,另一个工作台上的C#程序员,一个在VS中使用Blend另一个在VS中工作)是一个谬论。
能够快速重新设计UI的另一个谬误。它永远不会发生,它的过早优化。任何重大的返工都需要对视图模型进行大量的工作。调整是一回事,但是无法快速进行UI检修。视图模型必须符合交互范例,因此耦合比您可能意识到的要紧密。
对我来说,我使用MVVM来保持我的应用程序逻辑分离(我通常使用可测试的控制器和一组无逻辑的视图模型),但是需要任何代码和技巧来使我的UI以性感的方式运行。出汗。
否则,您最终只能进行设计,炫酷的动画等方面的控制,只是因为如何将MVVM简化的想法太令人难以置信了。
就像生活中的大多数事物一样,MVVM 在两个极端之间有一个甜蜜点。
软件设计中最重要的事情是尽早交付产品,找出使用了哪些功能,什么UI可以正常工作,而不要为没人使用的产品编写漂亮的代码。
如果您的应用程序要求您实时绑定过多的数据,那么MVVM实际上会妨碍您的工作,因为它引入了使过程变慢的抽象,并且假设我们现在正在谈论WPF / Silverlight / WP7,则绑定引擎目前效率不高;尽管增强功能正在不断发展。
就目前而言,MVVM也不是您需要考虑的方程式的唯一部分。Pure MVVM需要诸如Mediator模式之类的基础结构来支持,以允许您在断开连接的ViewModel之间进行通信。
尽管上面有blucz的意见,但MVVM在GoF模式中。如果您看一下这些模式,则MVVM等同于Model View Passive Presenter模式,该模式与MVVM大致同时出现。人们经常在这里抱怨MVVM的复杂性,纯粹是因为他们不了解如何使用它进行设计。
我发现MVVM的另一个问题是,您需要结合一种并非旨在支持该技术的第三方技术(例如MS Dynamics的UII框架)。有时,您不得不问自己,仅仅为了与MVVM一起工作而“入侵”另一项技术是否值得付出痛苦。
如果您要将Win Forms之类的东西混合并匹配到WPF解决方案中,则该解决方案的该部分可能也不适用于MVVM。我希望这可以使您对某些不适用于MVVM的领域有所了解。
在以下情况下,使用MVVM没有意义:
而已。我真的想不出为什么在使用WPF / Silverlight时为什么不使用MVVM,除非您在单独的项目中进行测试或调试。由于XAML绑定的工作方式,我发现设计模式非常适合WPF / Silverlight开发。
MVVM的全部意义在于,您的整个应用程序都在ViewModels中运行,而Views只是用户可以用来与ViewModels交互的漂亮层。您要单击一个按钮,实际上是在运行ViewModel方法。您要更改页面,实际上是在ViewModel中更改CurrentPage属性。
我将MVVM用于所有WPF / Silverlight开发,甚至是小型,简单的单页项目,尽管根据项目的规模和需要,我使用MVVM的方式也有所不同。一次我做一个没有MVVM的小型应用程序时,我最终对此感到后悔(后来重构为在添加更新时使用MVVM)
我为一个有代码隐藏项目的客户做了一些工作,试图将其转换为MVVM,这是一个巨大的混乱。这并不是说所有代码隐藏项目都是一团糟。视图将错误或崩溃,这会给设计人员造成麻烦。
我已经迷上了RoR,几乎不使用ASP.NET Webforms做任何事情,但是当ASP.NET MVC出来时,我尝试了尽可能多的学习,经常使用ASP.NET MVC In Action书籍和codecampserver作为参考。 。尽管这是一种不同的模式,但是我使用了从SL / MVVM开发的In Action书籍中学到的许多知识。
当我开始使用Silverlight时,我为它的裸露感到惊讶,这感觉就像是要求从众多可用的框架中进行选择的其中之一。我认为这是人们放弃学习MVVM的问题。
我从出色的MVVM-Light开始,这有助于我很好地掌握图案。后来我开始与Caliburn Micro合作,然后灯一直亮着。对我而言,Caliburn Micro就像在ASP.NET Webforms上使用ASP.NET MVC。因此,即使对于小型示例应用程序,我也可以创建项目NuGet CM,并且可以运行。我遵循ViewModel的第一种方法,并保持自己的Views哑巴并易于在Blend中使用。如果您参与其中,可以测试我的VM,而使用CM可以很轻松地围绕复杂的屏幕布局进行切换。
您可能会喜欢尝试这种替代方法。此选项避开了WPF数据绑定,数据模板和MVVM的方式。此选项更像旧的,简单的,可靠的WinForms designer.cs方法。
WPF复合材料
http://wpfcomposites.codeplex.com/
在这里,WPF的简洁C#代码隐藏是通过基于网格的组合将简单矩阵覆盖在UI之上而产生的。如果现代XAML UI仅包含几个文本标签和一个照片列表框,为什么不能通过简单的代码行定义它:grid.AddText(guid,x坐标,y坐标)?请注意,这不是在画布上,而是仍在WPF容器内:网格,扩展面板等。WPF的功能非常强大。这种方法仅利用了这一点。
开发人员通常不介意矩阵和索引。从由数据传输对象(DTO)或POCO的GUID定义的粗粒度容器级别开始,然后用其中的潜在行和列的细粒度矩阵对这些键控容器进行补充?
上面的codeplex项目从ListBox控件开始引入了这种方法,但是现在扩展到涵盖所有WPF复合控件。例如,每个listboxitem是一个添加了GUID的组合(一个组合与一个类一对一地关联),然后在该项目的内部有一个Grid,其上有子UI元素(子与属性一对一地关联)在一个类中)可以通过代码隐藏随意添加。子级可能是文本块或图像。
这个想法是利用索引和定义良好的UI元素结构(它强制以PresentationFramework.Aero主题的ListBox为基础)而不是数据模板。这种新方法故意限制了可以执行的操作,但是这样做却产生了直观的,简洁,健壮的C#代码。无需为样式化滚动条而寻找控制模板解决方案,也无需为简单任务而将具有多个数据模板的解决方案弄得一团糟。