Click here to Skip to main content
15,880,796 members
Articles / Web Development / IIS

WCF by Example - Chapter IX - WPF Client - Notify Property Change

Rate me:
Please Sign up or sign in to vote.
4.88/5 (14 votes)
11 Oct 2011CPOL5 min read 44.1K   36  
WPF INotifyPropertyChanged implementation.
PreviousNext
Chapter VIIIChapter X

The series

WCF by Example is a series of articles that describe how to design and develop a WPF client using WCF for communication and NHibernate for persistence purposes. The series introduction describes the scope of the articles and discusses the architect solution at a high level.

Chapter overview

In the previous chapter, we introduced the Relay Command, and we demonstrated how the Views using bindings in the XAML could easily invoke service methods. We also found a problem where the ViewModel was not notifying the View of the updates that took place in our Model when invoking the customer service methods. In this chapter, we will discuss a pattern that leverages the notification of Model changes to the View.

The source code for this chapter can be found at CodePlex change set 93477. The latest code for the eDirectory solution can be found at CodePlex.

As a result of the ServiceAdapter and CommandDispatch re-factor that was done in Chapter VII, the following changes were done:

Image 3

The INotifyPropertyChanged interface

The INotifyPropertyChanged interface can be used by XAML DataContext instances to notify the XAML View that a property in the model has changed. The interface is quite simple; it just exposes an event named "PropertyChanged", and the View subscribes to this event so it can be notified when the event is triggered. If we examine the event handler, we will see that it requires an object, in our case the ViewModel, and the name of property that has changed, this is passed as a string.

We could implement this interface in each of our ViewModel classes, but a better approach is to provide a base class that includes this function and a common implementation so it can be easily re-used.

The ViewModelBase

Image 4

The ViewModelBase abstract class implements INotifyPropertyChanged, which provides an easy mechanism for the ViewModel classes to indicate when the Model property has changed. The ViewModel is assigned as the View's DataContext in the ViewModel constructor, as can be seen below in the code snippet in line 01.

There are people indicating that the ViewModel should not be aware of the View, this is probably correct if the MVVM pattern is strictly followed. The main driver to isolate the ViewModel from the View is for testing purposes. You may want to see what others have already said regarding this topic:

Thread by mtaboy: why in your ViewModels you have a reference from its view. Thread by stl7 re-coupling Views and ViewModels.

In the MVVM Light Toolkit, one of the uses of the Messenger component is precisely to decouple the ViewModel from Views. It requires a little bit more of infrastructure but it resolves most of the problems when coupling is a problem.

C#
public CustomerViewModel()
{            
    CustomerServiceAdapter = new ServiceAdapter<ICustomerService>();
    Refresh();
01          View = new CustomerView { DataContext = this };
    View.ShowDialog();
}

When the Model changes, we need to invoke RaisePropertyChanged in the base class. There are many implementation examples of this pattern on the web, the most common implementation is to pass the property name in a string parameter. But if the property is renamed or removed as a result of some re-factor, we need to remember to update the code raising the event; this approach might not be the most adequate, so in our solution, we provide an overloaded method that uses Lambda Expressions and avoids the mentioned issues at compile time.

The ViewModelBase implementation is as follows:

C#
public class ViewModelBase :INotifyPropertyChanged
{
01  public event PropertyChangedEventHandler PropertyChanged = delegate { return; };

02  protected void RaisePropertyChanged(string propertyName)
    {
        VerifyPropertyExists(propertyName);
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

03  protected void RaisePropertyChanged<T>(Expression<Func<T>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;

        if (memberExpression == null)
            throw new ArgumentException("expression must be a property expression");

        RaisePropertyChanged(memberExpression.Member.Name);
    }

    [Conditional("DEBUG")]
04  private void VerifyPropertyExists(string propertyName)
    {
        PropertyInfo currentProperty = GetType().GetProperty(propertyName);
        string message = string.Format("Property Name \"{0}\" does not exist in {1}", 
                                       propertyName, GetType());
        Debug.Assert(currentProperty != null, message);
    }
}

The implementation of INotifyPropertyChanged is done in a single line (line 01). We use here a trick so we don't have to check if the event handler is null when notifying the event subscribers.

The method in line 02 is the common implementation of the RaisePropertyChanged pattern; a helper method (line 04) is used to notify if the property exists. The method in line 03 is an enhanced approach, the one mentioned before, that removes the need to pass property names in strings.

The implementation

Just a minor change in our CustomerViewModel is required to get it working with the new abstract class; with two lines, we will have our application working.

C#
public class CustomerViewModel
01      : ViewModelBase
{

    ...

    private void Refresh()
    {
        var result = CustomerServiceAdapter.Execute(s => s.FindAll());
        Model = new CustomerModel { NewCustomerOperation = new CustomerDto(), 
                                    CustomerList = result.Customers};
        
02      RaisePropertyChanged(() => Model);
    }
}

The first change is that CustomerViewModel now inherits from ViewModelBase (line 01), easy. The other change is when we want to notify the View that the Model has changed; this occurs when the FindAll method is called in the Refresh method. Line 02 indicates how this is achieved, a Lambda Expression is used to indicate which property of the ViewModel has been updated, in our case, Model. This is the only thing the View requires to be refreshed.

If we now execute the client and we enter some customer details like we did in the previous chapter, this time when the Save button is pressed, we can see how the client is refreshed and it displays the correct data in the grid. It is worth noting how the application looks after the New Customer controls; the Refresh method assigns a new blank instance of CustomerDto to the Model.NewCustomerOperation property which automatically resets the entered values, isn't it nice?

Image 5

Chapter summary

There is not more to discuss about the INotifyPropertyChanged pattern; it is simple and slick, which is good news. At this point, we have our application running. This is a major milestone in our series, it means we are in a position to demonstrate to our client our work; we are ready to engage them in an effective and productive manner; we could arrange workshops with the key users to demonstrate the new functionality. Try to convince them to use the application; as we mentioned, they can execute the application from a USB stick if required, it could not be any easier. And if you are lucky, they might start logging defects and enhancements in your ticket system. You may get busy at this stage, but be sure, you will get no major surprises for when the project reaches the UAT stage.

The next chapter is critical in the design of our application, Dependency Injection will be introduced in our solution. We need to remove the references to the server components in our client. Once we cover DI, we will be ready for covering the NHibernate and the WCF implementations. But remember, we don't need those two to gather the business requirements in an effective manner.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Ireland Ireland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --