How to Use AOP to simplify model and viewmodel design in mvvm

Source: Internet
Author: User

This article talks about a small but practical problem in mvvm, that is, how to simplify the design of model and viewmodel. This is a summary of some of our practices in the project.

[Note] the concept of mvvm is not the focus of this article. If you are not familiar with mvvm, refer to here. About the origins and comparisons between mvvm and the MVPs and MVC design patterns, and the general situation of several mainstream mvvm frameworks, I may take some time to sort out another article recently, if you are interested, take a look.

Anyone who has used mvvm knows that we need to define model and viewmodel in the project. A model refers to a data entity that stores data and provides interaction with external resources (such as databases or remote services. Viewmodel refers to a bridge between a view and a model. Generally, a view refers to an interface (such as a window in WPF or a page in Silverlight ), the core purpose of mvvm is to make the view design more independent. It should not contain too much data logic. In extreme cases, it should not have a custom lineCode. Then, you may ask how to display and update data without code? The answer is that the view is connected to the viewmodel through binding. In both WPF and Silverlight, two-way binding can be implemented. In this way, data can be displayed and updated. In addition, some commands can be made public in viewmodel so that special operations in view can be bound. --This is the core theory of mvvm..

 

So let's get down to the point. Before introducing our questions, let me show you a typical model type.

 Using System. diagnostics; Using System. componentmodel;Namespace Wpfmvvmsample. Models { Public   Class Employee: inotifypropertychanged { Public   Event Propertychangedeventhandler propertychanged; Private   Void Onpropertychanged ( String Name ){ If (Propertychanged! = Null ) Propertychanged ( This , New Propertychangedeventargs (name ));// To facilitate debugging, We output a line of information in the output window. Debug. writeline ( String . Format ( "{0} changed" , Name ));} Private   String Firstname = String . Empty; Public   String Firstname {get { Return Firstname;} set { If ( Value ! = Firstname) {firstname = Value ; Onpropertychanged ("Firstname" );}}} Private   String Lastname = String . Empty; Public   String Lastname {get { Return Lastname;} set { If ( Value ! = Lastname) {lastname = Value ; Onpropertychanged ( "Lastname" );}}}Private   Int Age = 18; Public   Int Age {get { Return Age;} set { If ( Value ! = Age) {age = Value ; Onpropertychanged ( "Age" );}}}}}

Are you familiar with this code? This is a model type that represents employee information.

    1. To implement bidirectional binding and receive notifications when attributes change, we usually need to implement an interface called inotifypropertychanged.
    2. This interface only has one event (propertychanged ). Generally, to trigger this event, we define a unified method (onpropertychanged)
    3. Generally, in the Set Method of each attribute, we need to call onpropertychanged to send a notification that the attribute has been changed.

This is not complicated, right? But the problem is that if the model type has many attributes, the class will become very lengthy. In addition, many codes are written in the same way. What's more, there may be many model types in a project?

 

Our question is: is there any way to automate such tasks? That is, the onpropertychanged method is automatically called after the set method of each attribute is executed.

 

For example, can we still define the model type as follows?

 
NamespaceWpfmvvmsample. Models {Public ClassCustomer {Public StringCustomerid {Get; set ;}Public StringCompanyName {Get; Set ;}}}

I think a model type should be so simple. Don't you think so? So, let me do an experiment step by step.

 

First, considering that there may be many model types, each type implements the inotifypropertychanged interface, which is not so ideal. To address this problem, we naturally think of extracting this part of implementation into a base class. We did. For example, here we define a modelbase type

 Using System. componentmodel;Using System. diagnostics; Namespace Wpfmvvmsample. Models { Public   Abstract   Class Modelbase: inotifypropertychanged { Public   Event Propertychangedeventhandler propertychanged; Protected   Void Onpropertychanged ( String Name ){ If (Propertychanged! = Null ) Propertychanged ( This ,New Propertychangedeventargs (name )); // To facilitate debugging, We output a line of information in the output window. Debug. writeline ( String . Format ( "{0} changed" , Name ));}}}

 

Next, let's let the customer type inherit modelbase

  namespace  wpfmvvmsample. models { Public   class  Customer: modelbase { Public   string  customerid {Get; set ;}  Public   string  companyName {Get; Set ;}}

We have simplified the work of implementing interfaces. However, there is another key point. How can we make the Set Method of each attribute automatically call the onpropertychanged method defined in the base class? That is to say, we want to insert a special code logic behind each set method. Is that true?

 

I think of a so-called AOP (Aspect-Oriented Programming) framework that I used before. I wrote an article at the beginning.ArticleFor more information, see the following link.

Application of postsharp AOP design in. Net remoting

I have used an industry-recognized static AOP framework called postsharp. Its official website is listed below

Http://www.sharpcrafters.com/

With postsharp, our problem can be easily solved. Download, install, and add two references to the project.

We can write the following special attribute

 Using System; Using Postsharp. Laos; Namespace Wpfmvvmsample { /// <Summary>      /// Chen xizhang      /// 2011-6-24      /// This is a special attribute and is a method for implementing method injection in postsharp.      /// </Summary> [Serializable] [attributeusage (attributetargets. Class, inherited = True , Allowmultiple = False )]Public   Class Policypropertychangeattribute: onmethodboundaryaspect { Public   Override   Void Onsuccess (methodexecutioneventargs eventargs) {var methodname = eventargs. method. Name; var type = eventargs. instance. GetType (); var targetmethod = type. getmethod ( "Onpropertychanged" , System. reflection. bindingflags. nonpublic | system. reflection. bindingflags. instance ); If (Methodname. startswith ( "Set _" ) & Targetmethod! = Null ) // Only inject this method. {Var propertyname = methodname. substring (4 ); // Parse the property name Targetmethod. Invoke (eventargs. instance, New [] {Propertyname }); // Execute this method }}}}

So how to implement this special attribute? We only need to add it to the customer type. The type of the finally designed customer is as follows:

  namespace  wpfmvvmsample. models {[policypropertychange]  Public   class  Customer: modelbase { Public   string  customerid {Get; set ;}  Public   string  companyName {Get; Set ;}}

You can compare this type with the employee type at the beginning of this article. The code is much simpler.

As for viewmodel, the same method is also required.

 

You may wonder, is that amazing? After this is done, does it actually take effect?

To make you see the results, it is complex. Do you still remember the following code in modelbase?

// To facilitate debugging, We output a line of information in the output window.Debug. writeline (String. Format ("{0} changed", Name ));

That is to say, as long as the attribute changes, a notification is sent and some information is displayed in the output window.

 

We can perform a simple test and add the following code to mainwindow:

 Using System; Using System. Collections. Generic; Using System. LINQ; Using System. text; Using System. windows; Using System. Windows. controls; Using System. Windows. Data; Using System. Windows. documents; Using System. Windows. input; Using System. Windows. Media; Using System. Windows. Media. imaging; Using System. Windows. Navigation; Using System. Windows. shapes; Namespace Wpfmvvmsample { /// <Summary>      /// Interaction logic for mainwindow. XAML      /// </Summary>      Public   Partial   Class Mainwindow: window { Public Mainwindow () {initializecomponent (); loaded + = New Routedeventhandler (mainwindow_loaded );} Void Mainwindow_loaded ( Object Sender, routedeventargs e) {var customer = New Models. Customer (); customer. customerid = "Microsoft" ; Customer. companyName = "Microsoft Company" ; This . Datacontext = Customer ;}}}

Here we initialize a customer object and make changes to both of its attributes.

Press F5 for debugging. Note that the output window is displayed. We will find the following output

This proves our assumption that after the attribute value is modified, we receive a notification and execute the onpropertychanged method.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.