Easy MVVM Example With INotifyPropertyChanged And INotifyDataErrorInfo

By | May 30, 2011

This is a very small MVVM tutorial. I used the MVVM Light (Galasoft) framework. I like this framework because, it brings a lot of very useful features.
The idea behind MVVM is to seperate the UI from the code. The most things I like are that your application is testable, “blendable” and you don’t have code in the code-behind file.

MVVM in short:
MVVM in short

Example:
First I create a base class for all models, which implements the interfaces INotifyPropertyChanged and INotifyDataErrorInfo.
I like to keep the Models clean (POCO). Sometimes it’s very difficult. You can implement the INotifyPropertyChanged interface in the ViewModel and wrap the model class. But this will lead to code duplication. That’s why I decided to implement the interfaces in the model in this example.

The base class for all models.

 
public class ModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
{
    #region Property changed
    public event PropertyChangedEventHandler PropertyChanged;

            protected void NotifyPropertyChanged(string propertyName, Action message)
    {
        if (this.PropertyChanged != null)
        { 
            // property changed
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            // send app message (mvvm light toolkit)
            if(message != null)
                message(this.IsValid);
        }
    }
    #endregion

    #region Notify data error
    private Dictionary> _errors = new Dictionary>();
    public event EventHandler ErrorsChanged;

    // get errors by property
    public IEnumerable GetErrors(string propertyName)
    {
        if (this._errors.ContainsKey(propertyName))
            return this._errors[propertyName];
        return null;
    }
        
    // has errors
    public bool HasErrors
    {
        get { return (this._errors.Count > 0); }
    }

    // object is valid
    public bool IsValid
    {
        get { return !this.HasErrors; }

    }

    public void AddError(string propertyName, string error)
    {
        // Add error to list
        this._errors[propertyName] = new List() { error };
        this.NotifyErrorsChanged(propertyName);
    }

    public void RemoveError(string propertyName)
    {
        // remove error
        if (this._errors.ContainsKey(propertyName))
            this._errors.Remove(propertyName);
        this.NotifyErrorsChanged(propertyName);
    }

    public void NotifyErrorsChanged(string propertyName)
    {
        // Notify
        if (this.ErrorsChanged != null)
            this.ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
    }
    #endregion
}

And here my model.

public class Customer : ModelBase
{
    private string _CustomerId;
    public string CustomerId
    {
        get { return this._CustomerId; }
        set
        {
            if (this._CustomerId != value)
            {
                if (value == "abc") 
                    base.AddError("CustomerId", "abc not allowed");
                else
                    base.RemoveError("CustomerId");

                this._CustomerId = value;
                base.NotifyPropertyChanged("CustomerId", new Action((valid) => { AppMessages.CustomerIsValid.Send(valid); }));
            }
        }
    }
    public string Name { get; set; }
}

And here is my ViewModel which represents a customer. This class inherits the base class ViewModelBase of the MVVM Light Toolkit. Beside the base class you will find in the following code snippet a second cool feature of MVVM Light framework.
The constructor registers a message. A message can be sent from everywhere and can be registered everywhere. This is very useful, when you don’t want to couple two classes. For instance class A sends a message and the class B can register the message without knowing the sender. In this example, I use this feature for validation. My Models are sending messages. For instance the customer sends the message “CustomerIsValid”. My ViewModel registers this message and executes the RaisePropertyChanged(“IsValid”) method.
Later you will see, that the “IsValid” property is used in the view. You see know that my Model raises the PropertyChanged event and my ViewModel tool. It is possible to do this only from my Model… or only from my ViewModel. But lets say you have a second Model called “Address” which you want to expose in the same ViewModel. And this Model has to be validated too. In my opinion, it’s easier to send message from the Model and register them in the ViewModel. The ViewModel executes the RaisePropertyChanged method and in the View you have only to bind the IsValid property, which returns true when customer and address are valid.

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        if (IsInDesignMode)
        {
            // Code runs in Blend --> create design time data.
        }
        else
        {
            // Code runs "for real"
            // Register message. (just run the PropertyChanged function. "arg" isn't important here.)
            AppMessages.CustomerIsValid.Register(this,
                new Action((arg) => { this.RaisePropertyChanged("IsValid"); }));

            this.Customer = new Customer();
        }
    }

    private Customer _customer;
    public Customer Customer
    {
        get { return _customer; }
        set { _customer = value; }
    }

    public bool IsValid
    {
        get { return this.Customer.IsValid; }
    }
}

(Sending/registering message is very easy..)

public class AppMessages
{
    public static class CustomerIsValid
    {
        public static void Send(bool argument)
        {
            Messenger.Default.Send(argument);
        }

        public static void Register(object recipient, Action action)
        {
            Messenger.Default.Register(recipient, action);
        }
    }
}

And last but no least.. the view:
Nothing special here. The DataContext “Locator” is another feature of MVVM Light. It is the entry point for bindings.



    
        
            
            
            
        
    

This is it!
Here are two printscreens.

Form is valid:
MVVM Validation OK

Form is not valid (error popup visible and save button is disabled):
MVVM Validation False


Download VS.NET Solution: Click here