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

为了掌握 MVVM 的概念,我已经阅读了一些博客,并且研究了一些项目。

据我所知,观景是愚蠢的,它只知道如何表示传递给它的内容。

模型 只是简单的数据,而 ViewModel就像是两者之间的填充,它应该从 模特获得信息并传递给 观景,而 观景应该知道如何呈现它。或者反过来,如果 观景中的信息发生变化,它应该将变化传递给 模特

但我还是不知道如何应用这个概念。谁能给我解释一个非常简单的场景,好让我理解这个概念?我已经看了几个项目,但它仍然没有完全的意义,所以如果有人可以写出它在简单的英语,这将是很好的。

41542 次浏览

I've written this out in about as "plain english" as I can think of in this series on MVVM. In particular, this diagram is likely the simplest, short explanation.

That being said, basically, the "model" is your data or business rules. It really shouldn't know about how or where it's going to be used, and especially not which technology is going to use it. The "model" is the core guts of the application - and it shouldn't need to worry about whether the application is WPF, Silverlight, Windows Forms, ASP.NET, etc - it's just "itself" in a pure form.

The "View" is the part that is completely technology specific. In MVVM, ideally, the View should be nearly 100% XAML, as this provides some huge gains for flexibility.

However, there needs to be something that translates the information from the Model into some form where it's usable by the technology at hand - this is where the ViewModel comes into play. For example, this often "wraps" up the model classes into a "ViewModel" for that specific data that includes Commands (for running logic), implements INotifyPropertyChanged (for data binding support), etc. That's it - it's the bridge that makes the Model usable by the View.

A great intro to MVVM can be found in Jason Dolinger's video here. I kept the video with me for quite a while when I was starting, it's really useful.

I like to think of it this way:

Views, as you say, are dumb. Josh Smith, writer of the seminal and often linked MSDN article on MVVM, has said that views are "the clothes that data wears." Views never actually contain data or directly manipulate it, they are just bound to properties and commands of your viewmodels.

Models are objects that model the domain of your application, as in business objects. Is your application a music store? Perhaps your model objects will be artists, albums and songs. Is your application an org-chart browser? Perhaps your model objects will be managers and employees. These model objects are not related to any kind of visual rendering, and they aren't even directly related to the application you're putting them into - your model objects should make sense completely on their own as a family of objects that represent some kind of domain. The model layer also typically includes things like service accessors.

This brings us to Viewmodels. What are they? They are objects that model a GUI application, meaning they provide data and functionality to be used by views. They are what define the structure and behavior of the actual application you are building. For the model objects, the domain is whatever domain you choose (music store, org-chart browser, etc.), but for the viewmodel, the domain is a graphical application. Your viewmodels are going to encapsulate the behavior and the data of everything your application does. They are going to expose objects and lists as properties, as well as things like Commands. A command is just a behavior (at its simplest, a method call) wrapped up into an object that carries it around - this idea is important because views are driven by databinding, which attaches visual controls to objects. In MVVM, you don't give a button a Click handler method, you bind it to a command object (served up from a property in a viewmodel) that contains the functionality you want to run when you click it.

For me, the most confusing bits were the following:

  • Even though the viewmodels are models of a graphical application, they don't directly reference or use visual concepts. For example, you don't want references to Windows controls in your ViewModels - those things go in the view. ViewModels simply expose data and behaviors to controls or other objects that will bind to them. For example - do you have a view with a ListBox in it? Your viewmodel is almost certainly going to have some kind of collection in it. Does your view have buttons? Your viewmodel is almost certainly going to have some commands in it.
  • There are a few kinds of objects that could be considered "viewmodels". The simplest kind of viewmodel to understand is one that directly represents a control or a screen in a 1:1 relationship, as in "screen XYZ has a textbox, a listbox, and three buttons, so the viewmodel needs a string, a collection, and three commands." Another kind of object that fits in the viewmodel layer is a wrapper around a model object that gives it behavior and makes it more usable by a view - this is where you get into the concepts of "thick" and "thin" viewmodel layers. A "thin" viewmodel layer is a set of viewmodels that expose your model objects directly to the views, meaning the views end up binding directly to properties on the model objects. This can work for things like simple, read-only views, but what if you want to have behavior associated with each object? You don't want that in the model, because the model isn't related to the application, it's only related to your domain. You can put it in an object that wraps your model object and offers up more binding-friendly data and behaviors. This wrapper object is also considered a viewmodel, and having them results in a "thicker" viewmodel layer, where your views never end up directly binding to anything on a model class. Collections will contain viewmodels that wrap models instead of just containing models themselves.

The rabbit hole goes deeper - there are lots of idioms to figure out like ValueConverters that keep MVVM working, and there's a lot to apply when you start thinking about things like Blendability, testing, and how to pass data around in your app and ensure that each viewmodel has access to the behavior it needs (this is where dependency injection comes in), but hopefully the above is a good start. The key is to think about your visuals, your domain, and the structure and behavior of your actual application as three different things.

Building a ViewModel that presents a consistent facade over the underlying Model can be a lot more complex than it looks. This article about building ViewModel objects demonstrates how to build a ViewModel, and illustrates some of the issues you're likely to encounter - along with what looks like reasonable solutions. When I read it the section on dealing with collections was missing, but it's still got some interesting points.

Using this incredibly helpful article as the source, here is a summary for View, ViewModel, and Model.


View:

  • The view is a visual element, such as a window, page, user control, or data template. The view defines the controls contained in the view and their visual layout and styling.

  • The view references the view model through its DataContext property. The controls in the view are data bound to the properties and commands exposed by the view model.

  • The view may customize the data binding behavior between the view and the view model. For example, the view may use value converters to format the data to be displayed in the UI, or it may use validation rules to provide additional input data validation to the user.

  • The view defines and handles UI visual behavior, such as animations or transitions that may be triggered from a state change in the view model or via the user's interaction with the UI.

  • The view's code-behind may define UI logic to implement visual behavior that is difficult to express in XAML or that requires direct references to the specific UI controls defined in the view.

NOTE:
Because the view model should have no explicit knowledge of the specific visual elements in the view, code to programmatically manipulate visual elements within the view should reside in the view's code-behind or be encapsulated in a behavior.


View Model:

  • The view model is a non-visual class and does not derive from any WPF or Silverlight base class. It encapsulates the presentation logic required to support a use case or user task in the application. The view model is testable independently of the view and the model.

  • The view model typically does not directly reference the view. It implements properties and commands to which the view can data bind. It notifies the view of any state changes via change notification events via the INotifyPropertyChanged and INotifyCollectionChanged interfaces.

  • The view model coordinates the view's interaction with the model. It may convert or manipulate data so that it can be easily consumed by the view and may implement additional properties that may not be present on the model. It may also implement data validation via the IDataErrorInfo or INotifyDataErrorInfo interfaces.

  • The view model may define logical states that the view can represent visually to the user.

NOTE:
Anything that is important to the logical behavior of the application should go into the view model. Code to retrieve or manipulate data items that are to be displayed in the view through data binding should reside in the view model.


Model:

  • Model classes are non-visual classes that encapsulate the application's data and business logic. They are responsible for managing the application's data and for ensuring its consistency and validity by encapsulating the required business rules and data validation logic.

  • The model classes do not directly reference the view or view model classes and have no dependency on how they are implemented.

  • The model classes typically provide property and collection change notification events through the INotifyPropertyChanged and INotifyCollectionChanged interfaces. This allows them to be easily data bound in the view. Model classes that represent collections of objects typically derive from the ObservableCollection<T> class.

  • The model classes typically provide data validation and error reporting through either the IDataErrorInfo or INotifyDataErrorInfo interfaces.

  • The model classes are typically used in conjunction with a service or repository that encapsulates data access and caching.