MVVM没有意义吗?[关闭]


91

正统的MVVM实现是没有意义的吗?我正在创建一个新的应用程序,并且考虑了Windows Forms和WPF。我选择WPF是因为它可以面向未来并且具有很大的灵活性。使用XAML的代码更少,可以更轻松地对UI进行重大更改。

由于WPF的选择是显而易见的,因此我认为,最好还是使用MVVM作为我的应用程序体系结构,因为它提供了可混合性,分离性和单元可测试性。从理论上讲,它看起来像UI编程的圣杯一样美丽。这短暂的冒险;但是,已经变成了真正的头痛。正如实践中所期望的,我发现我已经将一个问题换成了另一个。我倾向于成为一个痴迷的程序员,因为我想以正确的方式做事,以便获得正确的结果,并有可能成为更好的程序员。MVVM模式使我的生产力测试不及格,并且变成了一个巨大的麻烦!

最明显的例子就是增加了对“模态”对话框的支持。正确的方法是建立一个对话框并将其绑定到视图模型。使其难以工作。为了从MVVM模式中受益,您必须在应用程序的各个层中的多个位置分发代码。您还必须使用诸如模板和lamba表达式之类的深奥编程结构。东西使您凝视屏幕,挠挠头。正如最近发现的那样,这使得维护和调试成为一场噩梦。我有一个about框可以正常工作,直到我第二次调用它时出现异常,然后说它一旦关闭就无法再次显示该对话框。我必须为对话框的关闭功能添加一个事件处理程序,IDialogView实现中的另一个,最后是IDialogViewModel中的另一个。我以为MVVM可以将我们从如此奢侈的黑客中拯救出来!

有好几个人在为这个问题提供竞争解决方案,他们都是黑客,不能提供干净,易于重用的优雅解决方案。大多数MVVM工具箱都会掩盖对话框,当它们确实解决对话框时,它们只是警报框,不需要自定义界面或视图模型。

我打算放弃MVVM视图模式,至少是它的正统实现。你怎么看?如果您有任何麻烦,对您来说值得吗?我只是一个能力不强的程序员,还是MVVM并不是它所宣传的那样?


6
我一直在质疑MVVM是否设计过度。有趣的问题。
泰勒·莱斯

11
诸如MVVM和MVC之类的模式似乎过于工程化,直到您必须执行一些修改或更改组件为止。第一次必须这样做时,所有的仪式都是值得的
罗伯特·哈维

41
Lambda是深奥的吗?新闻给我。
Ray Booysen 2010年

5
@Ray-哈哈,对该评论+1!:D
Venemo 2010年

7
正如Alan Cooper十年前在About Face中所指出的那样,如果您正在设计UI,而模态对话框并不是一个极端的情况,那么您可能做错了什么。
罗伯特·罗斯尼

Answers:


61

抱歉,如果我的回答有点冗长,但不要怪我!您的问题也很长。

总之,MVVM不是毫无意义的。

最明显的例子就是增加了对“模态”对话框的支持。正确的方法是建立一个对话框并将其绑定到视图模型。使其难以工作。

是的,确实如此。
但是,MVVM为您提供了一种将UI外观与其逻辑分离的方法。没有人强迫您在任何地方使用它,也没有人拿着枪靠在您的额头上,以使您为所有内容创建单独的ViewModel。

这是我针对特定示例的解决方案:
UI如何处理特定输入与ViewModel无关。我将代码添加到View的.xaml.cs文件中,该文件实例化对话框并设置与其DataContext相同的ViewModel实例(或其他必要的对象)。

为了从MVVM模式中受益,您必须在应用程序的各个层中的多个位置分发代码。您还必须使用诸如模板和lamba表达式之类的深奥编程结构。

好吧,您不必在多个地方使用它。这是我要解决的方法:

  • 将XAML添加到视图中,.xaml.cs中没有任何内容
  • 在ViewModel内编写所有应用逻辑(可直接与UI元素配合的东西除外)
  • UI应完成但与业务逻辑无关的所有代码都放入.xaml.cs文件中

我认为MVVM的主要目的是将应用程序的逻辑和具体的UI分开,因此可以轻松修改(或完全替换)UI。
我使用以下原则:视图可以从ViewModel知道并假定它想要的任何东西,但是ViewModel可以知道关于View的全部信息。
WPF提供了一个很好的绑定模型,您可以使用它来实现这一目标。

(顺便说一句,正确使用模板和lambda表达式并不深奥。但是,如果您不想这样做,则不要使用它们。)

东西使您凝视屏幕,挠挠头。

是的,我知道这种感觉。刚看到MVVM时,我的感受正是如此。但是一旦掌握了它,它就不会再难过了。

我有一个关于盒子工作正常...

为什么将ViewModel放在about框后面?毫无意义。

大多数MVVM工具箱都会掩盖对话框,当它们确实解决对话框时,它们只是警报框,不需要自定义界面或视图模型。

是的,因为UI元素位于同一个窗口或另一个窗口中或当前正在绕火星运行这一事实与ViewModels无关。
关注点分离

编辑:

这是一个非常不错的视频,其标题是Build your own MVVM framework。值得一看。


2
最后三个字+1。但是其余的答案也很好。:)
罗伯特·哈维

12
+1以获取有关使用代码隐藏的建议。一个普遍的误解是,在MVVM中使用代码隐藏是“不好的”……但是对于纯UI相关的东西,这是要走的路。
托马斯·列维斯克

@托马斯:是的,我完全同意。我已经看到了几种实现方式,人们将所有代码(甚至是与UI相关的代码)都放入了ViewModel中,因为(根据它们)“就是代码所在的位置”。这很hacky。
Venemo

3
@Venemo,我认为您可以使用自定义行为之类的技术封装很多要放在代码后面的内容,如果您发现自己反复编写胶水代码,这将很方便。但是总的来说,我确实认为使用代码隐藏进行胶合比将笨拙的XAML结合在一起更好。在我看来,主要的担忧是确保代码背后没有什么复杂的东西足以保证进行单元测试。最好将所有足够复杂的东西封装在ViewModel或扩展类中,例如Behavior或MarkupExtension。
丹·布莱恩特

7
// @托马斯:是的,关于MVVM的最大误解是MVVM的目的是摆脱隐藏的代码。目的是使非UI代码脱离后面的代码。将仅UI的代码放在ViewModel中与将问题域代码放在后面的代码中一样糟糕。
Jim Reineri 2011年

8

使其难以工作。为了从MVVM模式中受益,您必须在应用程序的各个层中的多个位置分发代码。您还必须使用诸如模板和lamba表达式之类的深奥编程结构。

对于普通模态对话框?您肯定在那里做错了-MVVM的实现不必那么复杂。

考虑到您对MVVM和WPF都是陌生的,很可能您在各处都使用了次优的解决方案,并且不必要地使事情变得复杂-至少在我第一次使用WPF时,我是这样做的。在放弃之前,请确保问题实际上是MVVM,而不是您的实现。

MVVM,MVC,Document-View等是一种古老的模式系列。虽然有缺点,但没有您所描述的致命缺陷。


5

我正处于使用PRISM进行非常复杂的MVVM开发的过程中,因此我已经不得不解决此类问题。

我的个人结论:

MVVM与MVC / PopUps&co

  • MVVM确实是一个很好的模式,在大多数情况下,由于WPF中强大的数据绑定,它完全替代了MVC。
  • 在大多数情况下,直接从演示者调用服务层是合法的实现
  • 借助{Binding Path = /}语法,即使是非常复杂的List / Detail场景也可以通过纯MVVM实现。
  • 但是,当需要在多个视图之间实现复杂的协调时,必须使用控制器
  • 可以使用事件;暗示将IView(或AbstractObserver)实例存储在控制器中的旧模式已过时
  • 可以通过IOC容器将控制器注入每个Presenter中
  • 如果控制器的唯一用途是事件调度,则Prism的IEventAggregator服务是另一种可能的解决方案(在这种情况下,它可以完全替换控制器)
  • 如果要动态创建视图,这对于控制器来说是非常适合的工作(在棱镜中,控制器将被注入(IOC)IRegionManager)
  • 模态对话框在现代复合应用程序中大多已过时,除了真正的阻止操作(如强制性确认)外,其他都没有。在这些情况下,模式激活可以抽象为控制器内部调用的服务,并由专门的类实现,这还允许进行高级表示级单元测试。例如,控制器将调用IConfirmationService.RequestConfirmation(“您确定”),这将在运行时触发模式对话框显示,并且可以在单元测试中轻松模拟

5

我通过作弊来处理对话框问题。我的MainWindow实现了一个IWindowServices接口,该接口公开了所有特定于应用程序的对话框。然后,我的其他ViewModel可以导入服务接口(我使用MEF,但是您可以轻松地手动将接口通过构造函数传递)并使用它来完成必要的工作。例如,以下是我的一个小工具应用程序的界面:

//Wrapper interface for dialog functionality to allow for mocking during tests
public interface IWindowServices
{
    bool ExecuteNewProject(NewProjectViewModel model);

    bool ExecuteImportSymbols(ImportSymbolsViewModel model);

    bool ExecuteOpenDialog(OpenFileDialog dialog);

    bool ExecuteSaveDialog(SaveFileDialog dialog);

    bool ExecuteWarningConfirmation(string text, string caption);

    void ExitApplication();
}

这样可以将所有Dialog执行程序放在一个地方,并且可以很容易地存根以进行单元测试。我遵循对话框的客户端必须创建适当的ViewModel的模式,然后可以根据需要对其进行配置。Execute调用块,然后客户端可以查看ViewModel的内容以查看Dialog结果。

对于需要清洁绝缘和复杂结构的大型应用而言,更“纯”的MVVM设计可能很重要,但对于中小型应用,我认为一种实用的方法,加上适当的服务以暴露所需的挂钩即可, 。


但是你怎么称呼它呢?只在IWindowServices类的函数中构造对话框而不是传入对话框会更好。通过这种方式,调用模型视图不必了解有关特定对话框实现的任何信息。
乔尔·罗杰斯

该接口被注入到我需要访问应用程序对话框的任何ViewModel实例中。在大多数情况下,我将对话框传递给ViewModel,但是我确实有点懒,将WPF OpenFileDialog和SaveFileDialog用于文件对话框调用。我的主要目标是为了进行单元测试而隔离,因此对于该目标而言已足够。如果想要更好的隔离,则可能要创建一个OpenFileViewModel和SaveFileViewModel,它们将复制对话框的必要属性。
丹·布莱恩特

请注意,这绝对不是纯方法,因为使用对话框的ViewModel知道要打开的每个对话框的特定ViewModel。我觉得这很干净,但是您始终可以使用一个类来添加额外的隔离层,该类完全公开使用对话框所需的参数,并隐藏绑定期间使用的ViewModel属性的任何不必要的可见性。对于较小的应用程序,我觉得这种额外的绝缘是过大的。
丹·布莱恩特

5

设计模式可以帮助您,而不是阻碍。成为优秀开发人员的一小部分是知道何时“违反规则”。如果MVVM对于任务繁琐,并且您确定未来的价值不值得付出,则不要使用该模式。例如,正如其他张贴者所评论的那样,您为什么要花所有的开销来实现一个简单的“关于”框?

设计模式从来没有被教条式遵循。


2
正确。一个简单的about框应该很简单,但是如果您必须显示诸如版本,许可,运行过程,公司名称等信息,那该怎么办。这就是所有存在于某处的信息。在标准格式中,您可以绑定所有信息并使用它来完成。MVVM说您应该为其创建视图模型,这是多余的。
ATL_DEV 2011年

1

由于模式本身,MVVM很棒。但是WPF带有NET 4.0数据绑定支持的控件库非常有限,它比WinForm好很多,但是对于可绑定MVVM来说仍然不够,我想说它的功能大约是可绑定MVVM的30%。
可绑定的MVVM:这是UI,其中ViewModel仅使用数据绑定与ViewModel连接在一起。
MVVM模式是关于ViewState的对象表示的,它没有描述如何维护View和ViewModel之间的同步,在WPF中它是数据绑定,但是可以是任何东西。实际上,您可以在支持事件/回调的任何UI工具包中使用MVVM模式,可以在WinForms的纯WinAPI中使用它(我这样做了,事件/回调没有太多用处),甚至可以在文本中使用它。控制台,例如使用MVVM模式重写DoS的Norton Commander。

简而言之:MVVM不是毫无意义的,它很棒。NET 4.0 WPF的控件库很垃圾。

这是ViewModel的简单证明,您无法使用WPF以纯MVVM方式进行数据绑定。

public class PersonsViewModel
{
    public IList<Person> PersonList;
    public IList<ColumnDescription> TableColumns;
    public IList<Person> SelectedPersons;
    public Person ActivePerson;
    public ColumnDescription SortedColumn;
}

您无法对WPF的DataGrid列标题进行数据绑定,也无法对选定的行进行数据绑定等,您将可以通过简单的代码来完成此操作,或者为这5行最简单的ViewModel编写200行XAML hack代码。您只能想象复杂的ViewModel如何使情况变得更糟。
因此,除非您正在编写Hello World应用程序,否则答案是简单的,在WPF中使用可绑定MVVM是没有意义的。您将花费大部分时间思考如何将ViewModel绑定到hack。数据绑定是不错的方法,但请准备好回退到事件的70%的时间。


您可以使用转换器将其绑定到DataGrid。
卡梅伦·麦克法兰

@CameronMacFarland:并非全部,有些属性是只读且不可绑定的,有些根本不存在,并且只有事件报告状态更改。
Alex Burtsev 2011年

我承认我没有太多使用WPF DataGrid的经验。我倾向于避免使用它,因为它丑陋且不再适合WPF。话虽这么说,将转换器和AttachedProperties结合起来处理事件应该可以为您提供所需的东西。
卡梅伦·麦克法兰

1
Alex,您遇到的问题是DataGrid的设计,而不是MVVM。说“数据绑定很好,但是准备好回退到事件的70%时间”是完全错误的。我已经写了一些客观上庞大的WPF应用程序,其中UI中根本没有事件处理程序-((Telerik)数据网格初始化所需的事件处理程序除外)。
罗伯特·罗斯尼

3
我认为,如果您不采取“这设计不好并且不起作用”的态度,而不是采取这种态度,那么您可能会获得更大的成功,“为什么这对其他人有用,但对我却不有用?” 您可能会发现难以执行操作的原因是您尚不知道如何执行操作。
罗伯特·罗斯尼

0

不,这不是没有意义的,但是即使模式本身非常简单,也很难把头缠起来。那里有大量的错误信息,各个团体都在为正确的方法而战。我认为使用WPF和Silverlight时,您应该使用MVVM,否则您将过度编码并尝试在新模型中解决问题,而“旧的”获胜形式方法只会使您陷入麻烦。在Silverlight中,情况更是如此,因为所有内容都必须是异步的(可以进行破解,但您应该选择其他平台)。

我建议阅读这篇文章 仔细使用ViewModel Pattern简化WPF TreeView,以了解如何很好地实现MVVM,并允许您将胜利形式的思维方式转变为MVVM中的新思维方式。简而言之,当您想完成某件事时,首先将逻辑应用于ViewModel而不是View。您要选择一个项目吗?更改图标?不要遍历UI元素,只需更新models属性,然后让数据绑定变得精妙。


-1

当涉及到(模态)对话框时,我已经在许多MVVM实现中看到了相同的问题。当我看MVVM模式的参与者时,我会感觉缺少一些东西来构建一个一致的应用程序。

  • 视图包含特定的GUI控件,并定义了用户界面的外观。
  • ViewModel表示演示文稿的状态和行为。
  • 模型可以是来自域层的业务对象,也可以是提供必要数据的服务。

但是缺少的是:

  • 谁创建ViewModels?
  • 谁负责应用程序工作流程?
  • 当需要彼此通信时,谁在ViewModel之间进行调解?

我的方法是引入一个(用例)控制器,该控制器负责缺少的点。在WPF应用程序框架(WAF)示例应用程序中可以看到其工作方式。


Josh Smith实现的Mediator模式解决了我所有的View Model通信问题。Messenger.NotifyColleagues提供了一种拥有完全独立的视图模型的方法,该模型知道如何响应全局事件(如果他们关心的话),而无需任何两个视图模型彼此了解。它已经保存了我们的培根几次。
JasonD'5
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.