The Model-View-viewmodel (mvvm) design mode describes how to create a Windows Presentation Foundation or Microsoft Silverlight application.Program. Robert McCarter demonstrates the working principle of viewmodel and discusses how to use yourCodeAdvantages and disadvantages of viewmodel implementation.
PS: This article is from msdn, written by Robert McCarter. The specific address is unknown, I just re-posted the offline version. However, this article describes in detail how to operate and implement mvvm. it's really a rare article
Windows Presentation Foundation (WPF) and Silverlight provide a wide range of APIS for building modern applications, however, it may be very difficult to understand and apply all the WPF features harmoniously to build well-designed and easy-to-maintain applications. Where to start? What method is the correct application design method?
Model-View-viewmodel (mvvm) design mode describes common methods for building WPF or Silverlight applications. It is also a powerful tool for building applications and a common language for discussing application design with developers. Although mvvm is useful, it has not been developed for a long time and users have not yet formed a correct understanding.
When is the mvvm design mode applicable and not necessary? How should I design the application structure? How much code should be written and maintained on the viewmodel layer, and how can we reduce the amount of code on the viewmodel layer? How can we properly process the relevant attributes in the model? How do I display the set in the model in the view? Where should I instantiate the viewmodel object and link it to the model object?
In this article, I will explain how viewmodel works and discuss the advantages and disadvantages of implementing viewmodel in your code. I will also introduce some specific examples to demonstrate how to use viewmodel as the document manager to display model objects in the view layer.
------------------------------------------------
Model, viewmodel, and view
So far, each WPF and Silverlight application I have designed has the same high-level component design. Model is the core of applications and requires a lot of effort to design based on the Object-Oriented Analysis and Design (OOAD) best practices.
For me, model is the core of an application and represents the largest and most important business asset, because it records all complex business entities, their relationships, and their functions.
The value above the model is viewmodel. Viewmodel has two main objectives: to enable the model to be easily used by the WPF/XAML view; to separate the model from the view and encapsulate the model. These goals are certainly very good, but sometimes they cannot be achieved for some practical reasons.
The viewmodel you build knows how the user interacts with the application on the top. However, viewmodel knows nothing about view, which is an important part of mvvm design pattern. This allows Interaction designers and graphic designers to create a beautiful and effective UI Based on viewmodel. At the same time, they work closely with developers to design appropriate viewmodel to support their work. In addition, the separation of view and viewmodel makes viewmodel more conducive to unit test and reuse.
To strictly separate the model, view, and viewmodel layers, I like to build each layer into a separate Visual Studio project. With reusable utilities, Major executable assembly, and any unit test item (You Have A Lot Of This, right ?) After integration, this will generate a large number of projects and assemblies, as shown in 1.
Figure 1 components of an mvvm Application
Because this strict separation method produces a large number of projects, it is obviously the most suitable for large projects. For small applications with only one or two developers, the benefits of this strict separation may not offset the inconvenience caused by the creation, configuration, and maintenance of multiple projects, therefore, separating your code from different namespaces of the same project may be easier than fully isolating your code.
It is not easy to write and maintain viewmodels. You should not treat them rashly. However, answers to some of the most basic questions (when the mvvm design mode is applicable and when it is unnecessary) are often included in your domain model.
In large projects, domain models can be very complex and hundreds of classes need to be carefully designed so that they can be used in any type of applications (including web services, WPF, or ASP.. NET applications. A model may consist of several compatible assemblies, and even in super-large organizations, a domain model is sometimes built and maintained by a dedicated development team.
If you have a complex large domain model, introducing the viewmodel layer will always bring about benefits.
On the other hand, the domain model is sometimes very simple and may only be a thin layer covering the database. Class can be automatically generated, and usually implement inotifypropertychanged. The UI is usually a series of lists or tables that can be edited, allowing you to operate on the underlying data. Microsoft toolset has always been extremely good at easily and quickly building such applications.
If your model or application is of this type, viewmodel is likely to bring unacceptable high-end sales, but it does not have sufficient benefits for your application design.
However, even in these cases, viewmodel still has its value. For example, viewmodel is ideal for "undo. In addition, you can also choose to use mvvm to directly provide the model to the view in a certain part of the application (such as document management, which I will discuss later.
----------------------------------------------------------------------------------
Why use viewmodel?
Even if viewmodel looks suitable for your application, you still have questions to answer before you start coding. The most important issue is how to reduce the number of proxy attributes.
Mvvm design mode separates view from model, which is an important and valuable aspect of the model. Therefore, if the model class has 10 attributes that need to be displayed in the view, the viewmodel class usually has 10 equivalent attributes, which only represent calls to the underlying model instance. When setting these proxy properties, the property change event is often triggered to notify the view that the property has been changed.
Not all model attributes require the viewmodel proxy attribute, but each model attribute that needs to be displayed in the view usually has a proxy attribute. Proxy attributes are generally as follows:
Public String description {get {return this. underlyingmodelinstance. description;} set {This. underlyingmodelinstance. Description = value; this. raisepropertychangedevent ("Description ");}}
Any slightly more complex application will have dozens or hundreds of model classes. These classes need to be shown to users through viewmodel. This is the essence of separation provided by mvvm.
Writing these proxy properties is cumbersome, so it is prone to errors, especially when a property change event is triggered, a string is required, this string must match the attribute name (and will not be included in any Automatic Code refactoring ). To eliminate these proxy events, a common solution is to display the model instance directly from the viewmodel package, and then enable the domain model to implement the inotifypropertychanged interface:
Public class someviewmodel {public someviewmodel (domainobject) {contract. Requires (domainobject! = NULL, "the domain object to wrap must not be null"); this. wrappeddomainobject = domainobject;} public domainobject wrappeddomainobject {Get; private set ;}...
Therefore, viewmodel can still provide the commands and more attributes required for the view, without repeating the model attribute or creating a large number of proxy attributes. This method is of course attractive, especially when the model class has implemented the inotifypropertychanged interface. Making the model implement this interface is not necessarily a bad thing. It is even a common practice in Microsoft. NET Framework 2.0 and Windows Forms applications. Although it can make the domain model messy, it is indeed useful for ASP. NET applications or domain services.
With this method, view has a certain degree of dependence on the model, but this is only an indirect dependency implemented through data binding, and does not need to reference the model project from the View Project. Therefore, this method is sometimes useful from a purely practical perspective.
However, this method actually violates the mvvm design pattern and will reduce your ability to introduce new viewmodel features (such as the "undo" feature) in the future. I have encountered a situation where this method leads to a lot of rework. Imagine that this is not uncommon: there is a data binding on the deep nested attribute. If the person viewmodel is the current data context and the person has the address, the Data Binding may be as follows:
{Binding wrappeddomainobject. Address. Country}
If you need to introduce more viewmodel functions on the address object, you need to delete the data binding reference to wrappeddomainobject. Address and use the new viewmodel attribute instead. This will cause problems because it is difficult to test the update of the XAML data binding (which may also include the data context. The view component does not have an automated full regression test.
--------------------------------------------------------------
Dynamic attributes
My solution to excessive proxy attributes is to use the new. NET Framework 4 and WPF that supports dynamic object and dynamic method scheduling. The latter enables you to determine how to process read/write operations for attributes that do not exist in the class at runtime. This means that you can eliminate all handwritten proxy attributes in viewmodel and still encapsulate the underlying model. However, note that Silverlight 4 does not support binding to dynamic attributes.
The simplest way to implement this function is to allow the viewmodel base class to expand the new system. Dynamic. dynamicobject class and override trygetmember and trysetmember members. When the referenced attribute does not exist in the class, the dynamic Language Runtime (DLR) will call these two methods so that the class can decide how to implement the missing attribute at runtime. With a small amount of reflection, you only need to write a few lines of code, And the viewmodel class can dynamically proxy access to the attribute of the underlying model instance:
Public override bool trygetmember (getmemberbinder binder, out object result) {string propertyname = binder. name; propertyinfo property = This. wrappeddomainobject. getType (). getproperty (propertyname); If (property = NULL | property. canread = false) {result = NULL; return false;} result = property. getvalue (this. wrappeddomainobject, null); Return true ;}
At the beginning of this method, reflection is used to find the attributes on the underlying model instance. If this model does not have such an attribute, this method fails and returns false, and data binding also fails. If an attribute exists, this method uses the attribute information to retrieve and return the attribute value of the model. Compared with the get method of traditional proxy attributes, this is an extra task, but it is also the only implementation you need to write for all models and attributes.
The real strength of the dynamic proxy property method lies in the property configurator. In trysetmember, You can include common logic, such as triggering a property change event. The Code is as follows:
Public override bool trysetmember (setmemberbinder binder, object Value) {string propertyname = binder. name; propertyinfo property = This. wrappeddomainobject. getType (). getproperty (propertyname); If (property = NULL | property. canwrite = false) return false; property. setvalue (this. wrappeddomainobject, value, null); this. raisepropertychanged (propertyname); Return true ;}
Similarly, this method uses reflection to get attributes from the underlying model instance at the beginning. If the property does not exist or is read-only, this method fails and returns false. If the attribute exists on the domain object, the model attribute is set using the attribute information. Then you can include the logic that is common to all attribute setters. In this sample code, I only triggered a property change event for the property you just set, but you can easily complete more tasks.
To be continued .......