MVVM 毫无意义吗?

正统的 MVVM 实现毫无意义吗?我正在创建一个新的应用程序,我考虑了 Windows 窗体和 WPF。我之所以选择 WPF 是因为它不会过时,而且提供了很大的灵活性。使用 XAML 代码更少,更容易对 UI 进行重大更改。

由于 WPF 的选择是显而易见的,我认为我可以一直使用 MVVM 作为我的应用程序架构,因为它提供了可混合性、分离性和单元测试性。从理论上讲,它看起来像是 UI 编程的圣杯。然而,这次短暂的冒险却变成了一件令人头疼的事。 不出所料,我发现我已经用一个问题换来了另一个问题。我倾向于成为一个执着的程序员,因为我想用正确的方式做事情,这样我就可以得到正确的结果,并且可能成为一个更好的程序员。MVVM 模式刚刚在我的生产力测试中不及格,并且已经变成了一个令人讨厌的大黑客!

最明显的例子就是添加对 Modal 对话框的支持。正确的方法是放置一个对话框并将其绑定到一个视图模型。让这个起作用很难。为了从 MVVM 模式中获益,您必须将代码分布在应用程序各层的多个位置。您还必须使用深奥的编程构造,如模板和 lamba 表达式。让你盯着屏幕抓耳挠腮的东西。正如我最近发现的那样,这使得维护和调试成为一个等待发生的噩梦。我有一个 about 框工作得很好,直到第二次调用它时出现异常,说它关闭后不能再次显示该对话框。我必须为对话窗口的关闭功能添加一个事件处理程序,在其 IDialogView 实现中添加另一个事件处理程序,最后在 IDialogViewModel 中添加另一个事件处理程序。我以为 MVVM 会把我们从这种铺张浪费的黑客手法中拯救出来!

有几个人对这个问题提出了相互竞争的解决方案,他们都是些技巧,不能提供一个干净的、易于重用的、优雅的解决方案。大多数 MVVM 工具包都掩盖了对话框,当它们确实处理对话框时,它们只是警告框,不需要自定义接口或视图模型。

我打算放弃 MVVM 视图模式,至少放弃它的正统实现。你觉得怎么样?如果你有孩子的话,你觉得值得吗?我只是一个无能的程序员还是 MVVM 不是它所宣传的那样?

13460 次浏览

Sorry if my answer became a little lenghty, but don't blame me! Your question is lengthy as well.

In summary, MVVM is not pointless.

The clear case in point is adding support for a Modal dialog box. The correct way is to put up a dialog box and tie it to a view model. Getting this to work is difficult.

Yes, it truly is.
However, MVVM provides you a way for separating the UI's appearance from its logics. Noone forces you to use it everywhere, and noone is holding a gun against your forehead to make you create a separate ViewModel for everything.

Here is my solution for this particular example:
How the UI handles a certain input is none of the ViewModel's business. I would add code to the View's .xaml.cs file, which instantiates the dialog box and sets the same ViewModel instance (or something else, if needed) as its DataContext.

In order to benefit from the MVVM pattern, you have to distribute code in several places throughout the layers of your application. You also have to use esoteric programming constructs like templates and lamba expressions.

Well, you don't have to use it in several places. This is how I would solve it:

  • Add the XAML to the View, and nothing in the .xaml.cs
  • Write every app logic (except the stuff that would directly operate with UI elements) inside a ViewModel
  • All the code that should be done by the UI but has nothing to do with business logic goes into the .xaml.cs files

I think that the purpose of MVVM is primarily to separate the logic of the application and the concrete UI, therefore enabling easy modifications (or complete replacement) of the UI.
I use the following principle: the View can know and assume anything it wants from the ViewModel, but the ViewModel can know NOTHING about the View.
WPF provides a nice binding model which you can use to achieve exactly that.

(BTW, templates and lambda expressions aren't esoteric if used right. But if you don't want to, don't use them.)

Stuff that makes you stare at the screen scratching your head.

Yeah, I know the feeling. Exactly what I was feeling when I first saw MVVM. But once you get the hang of it, it won't feel bad anymore.

I had an about box working fine ...

Why would you put a ViewModel behind an about box? No point in that.

Most of the MVVM toolkits gloss over dialogs and when they do address them, they are just alert boxes that don’t require custom interfaces or view models.

Yes, because the very fact that a UI element is in the same window, or another window, or is orbitting Mars at the moment is none of the ViewModels' concern.
Separation of Concerns

EDIT:

Here's a very nice video the title of which is Build your own MVVM framework. It's worth watching.

I have seen the same issue with a lot MVVM implementations when it comes to (modal) dialogs. When I look at the participants of the MVVM Pattern then I have the feeling that something is missing to build a coherent application.

  • View contains the specific GUI controls and defines the appearance of the user interface.
  • ViewModel represents the state and behavior of the presentation.
  • Model can be a business object from the domain layer or a service which provides the necessary data.

But missing is:

  • Who creates the ViewModels?
  • Who is responsible for the application workflow?
  • Who mediates between ViewModels when they need to communicate with each other?

My approach is to introduce a (Use-Case) Controller which is responsible for the missing points. How this works can be seen at the WPF Application Framework (WAF) sample applications.

Getting this to work is difficult. In order to benefit from the MVVM pattern, you have to distribute code in several places throughout the layers of your application. You also have to use esoteric programming constructs like templates and lamba expressions.

For a commonplace modal dialog box? You are certainly doing something wrong there - MVVM implementation doesn't have to be that complex.

Considering you are new to both MVVM and WPF, it is likely you are using suboptimal solutions everywhere and unnecessarily complicate things - at least I did so when I first went WPF. Make sure that the problem is really MVVM and not your implementation before giving up.

MVVM, MVC, Document-View, etc. is an old family of patterns.. There are drawbacks, but no fatal flaws of the kind you describe.

I deal with the dialogs issue by cheating. My MainWindow implements an IWindowServices interface that exposes all of the application-specific dialogs. My other ViewModels can then import the services interface (I use MEF, but you could easily just pass the interface through constructors manually) and use it to accomplish what is necessary. For instance, here is what the interface looks like for a little utility application of mine:

//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();
}

This puts all of the Dialog executions in one place and it can be easily stubbed out for unit testing. I follow the pattern that the client of the dialog has to create the appropriate ViewModel, which they can then configure as needed. The Execute call blocks and afterward the client can look at the contents of the ViewModel to see the Dialog results.

A more 'pure' MVVM design may be important for a large application, where you need cleaner insulation and more complex composition, but for small to medium sized apps, I think a practical approach, with appropriate services to expose required hooks, is quite sufficient.

No, it’s not pointless, but it is difficult to wrap your head around even though the pattern itself is ridiculously simple. There are tons of misinformation out there and various groups who battle over the proper way. I think with WPF and Silverlight you should use MVVM or you’ll be over coding and attempting to solve problems in a new model the “old” win forms methodology which just leads you into trouble. This is more the case in Silverlight since everything is required to be asynchronous (hacks around this are possible, but you should just pick another platform).

I’d suggest reading this article Simplifying the WPF TreeView by Using the ViewModel Pattern carefully to see how MVVM can be implemented well and allow you to change your win forms mentality to the new way of thinking in MVVM. In short when you want to get something done apply the logic to the ViewModel first not the View. You want to select an item? Change an icon? Don’t iterate over UI elements, just update the models properties and let data-binding do the nitty gritty.

As the pattern itself MVVM is great. But WPF's control library shipped with NET 4.0 data binding support is very limited, it is a lot better than WinForm, but still it's not enough for bindable MVVM, I would say it's power is about 30% of what is needed for bindable MVVM.
Bindable MVVM : it's UI where ViewModel is wired with View only using data binding.
MVVM pattern is about the object representation of the ViewState, it doesn't describe how you maintain the sync between View and ViewModel, in WPF it's data binding but it can be anything. And actually you can use MVVM pattern in any UI toolkit which support's events\callbacks, you can use it in pure WinAPI in WinForms (I did, and it's not much more work with events\callbacks), and you can even use it in Text Console, like rewrite DoS's Norton Commander using MVVM pattern.

In short: MVVM is not pointless, it's great. NET 4.0 WPF's control library is trash.

Here is the simple proof of concept ViewModel which you can't data bind in pure MVVM manner using WPF.

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

You can't data bind WPF's DataGrid column headers, you can't data bind selected rows, etc etc, you will either do it in code simple way, or write a 200 lines of XAML hack code for these 5 lines of simplest ViewModel. You can only imagine how things get worser with complex ViewModels.
So the answer is simmple unless you are writing Hello World application, using bindable MVVM in WPF is pointless. You will spent most of your time thinking on hack to bind you ViewModel. Data binding is nice but be ready to fallback to event's 70% of time.

I am in the middle of a quite complex MVVM development using PRISM so I already had to cope with this kind of concerns.

My personal conclusions:

MVVM vs MVC /PopUps & co

  • MVVM is really a great pattern and in most cases it completely replaces MVC thanks to the powerful data binding in WPF
  • Calling your service layer directly from the presenter is a legitimate implementation in most cases
  • Even quite complex List /Detail scenarios may be implemented by pure MVVM thanks to the {Binding Path=/} syntax
  • Nonetheless, when complex coordination between multiple views needs to be implemented, a controller in mandatory
  • Events may be used; the old pattern which implies storing IView (or AbstractObserver) instances in the controller is obsolete
  • The controller can be injected in each Presenter by IOC container
  • Prism’s IEventAggregator service is another possible solution if the only use of the controller is event dispatching (in this case it can completely replace the controller)
  • If views are to be dynamically created, this is a very well suited job for the controller (in prism the controller will get injected (IOC) a IRegionManager )
  • Modal dialog boxes are mostly obsolete in modern composite applications, except for really blocking operations like mandatory confirmations; in these cases modal activation can be abstracted as a service called inside the controller, and implemented by a specialized class, which also allows for advanced presentation-level unit testing. The controller will, for example, call IConfirmationService.RequestConfirmation(“are you sure”) which will trigger a modal dialog display at runtime and can be easily mocked during unit testing

Design patterns are there to help you, not hinder. A small part of being a good developer is knowing when to "break the rules". If MVVM is cumbersome for a task and you have determined the future value isn't worth the effort, then don't use the pattern. For example, as other posters have commented, why would you go through all the overhead to implement a simple about box?

Design patterns were never intended to be dogmatically followed.