(X) Hide this
    • Login
    • Join
      • Say No Bots Generate New Image
        By clicking 'Register' you accept the terms of use .

Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer

(13 votes)
Zoltan Arvai
>
Zoltan Arvai
Joined Jul 20, 2009
Articles:   7
Comments:   5
More Articles
10 comments   /   posted on Jun 17, 2010

This article is compatible with the latest version of Silverlight.

Introduction

In this short series of articles I’ll cover what MVVM is and how to use it in practice, how to solve issues when applying this pattern and how to take advantage from it.

In the previous articles we created a simple master details scenario with the MVVM pattern. We used mock and live data sources with WCF, we created unit tests, we used Messaging from the MVVM Light Toolkit.

By now, we have an application that communicates with a server, and we know how to send data back to a server. This raises another questions like, are we sending back valid data?

Validation in MVVM

Let’s think about this a little bit. How and what do we want to validate? Let’s see. We have an Author, a Title, and a Price. Well we can set the price property to a negative value, which is probably not a valid scenario. In Silverlight 3 we can do validation in property setters. What we basically do is throwing exceptions in the setter if the value is not valid. This kind of validation has two problems.

  1. You throw exceptions to validate in property setters…. well… yeah…
  2. You can’t support full entity validation. You need to do workarounds to support that scenario.

Well, in Silverlight 4 we have an IDataErrorInfo interface, that supports validation, without throwing exceptions. Also you can centralize your validation rules.

Let’s validate the Price property! If the price is lower than zero, then this value is invalid.

 //Modify the BookDetailsViewModel class to implement IDataErrorInfo
 public class BookDetailsViewModel : ViewModelBase, IDataErrorInfo
  
 //Implement IDataErrorInfo
 
 public string Error
 {
     get { return null; }
 }
   
 public string this[string columnName]
 {
     get 
     {
         if (columnName == "Price")
         {
             //We have a validaton error
             if (Price < 0) return "Price must be positive";
         }
         //Everthing is okay
         return null;
     }
 }

Also you have to modify the binding for the textbox displaying the price. You need to add the ValidatesOnDataErrors=True option, to enable the use of IDataErrorInfo based validation.

 <TextBox Grid.Column="1" TextWrapping="Wrap" Grid.Row="2" d:LayoutOverrides="Height" 
          VerticalAlignment="Center" Margin="15,0"
          Text="{Binding Price, Mode=TwoWay, ValidatesOnDataErrors=True}"/>

You can create you own validation handler classes and centralize the validation logic as much you want. However this kind of validation supports synchronous validation only. Which means, the binding happens, the validation happens and that’s it. If there is a later change in the validation state, this scenario cannot be applied. Let me give you an example. You have the Title property. Let’s assume we have an Update operation. When we update a book, we have to make sure, that the new title we enter is not the same as any other title. The title should be unique (well not in real life, but right now it does :D). You can’t validate this one on the client side! You have thousands of books in the database. You have to run a check against a huge amount of data and it might take more than a second. Which means that when the user sets the title property, we should let him do it. But immediately we should run an asynchronous check to validate the title. If the check’s result is that the title entered is not valid, then we have to indicate it somehow.

Also what if I need more detailed error report, not just a simple string? What if a change in a property changes another property bound to the UI and now the other property’s value is invalid? I should indicate it somehow.

IDataErrorInfo does not support these scenarios. Fortunately there is an other interface that supports all these scenarios. It’s called INotifyDataErrorInfo.

This is a little bit more complex, so let’s go step by step.

 public interface INotifyDataErrorInfo
 {
     bool HasErrors { get; }
     event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
     IEnumerable GetErrors(string propertyName);
 }

There are two very important members here The ErrorsChanged event, and the GetErrors method!

GetErrors() returns a list of errors of a specific property, it can be a list of strings, or a list of custom objects, it’s up to us. The ErrorsChanged event should be raised whenever a new validation error should be added, or a validation error should be removed. How you handle the validation errors’ list is pretty much up to you.

Let’s implement it. However this will need a lot of additional code that is not specific to a certain ViewModel, so most of the code, I’ll put into an extended ValidatingViewModelBase class which implements INotifyDataErrorInfo interface and inherits ViewModelBase.

First I’ll need a list that can contain the errors for each property.

 public class ValidatingViewModelBase : ViewModelBase, INotifyDataErrorInfo
 {
     //Key is propertyName, value is list of validation errors
     protected Dictionary<string, List<string>> errorList = new Dictionary<string, List<string>>();

The key of the dictionary is the name of the property. The Value is a list of errors represented by simple strings.

I have an error if I have something in the Values inner collection.

 public bool HasErrors
 {
     get
     {
         //If there is any validation error
        return errorList.Values.Count > 0;
    }
 }

Now I need to implement the GetErrors() method:

 /// <summary>
 /// Gets the list of validation errors for a property
 /// </summary>
 /// <param name="propertyName">Name of the propety</param>
 public System.Collections.IEnumerable GetErrors(string propertyName)
 {
     CheckErrorCollectionForProperty(propertyName);
  
     //return related validation errors for selected property.
     return errorList[propertyName];
 }
   
 protected void CheckErrorCollectionForProperty(string propertyName)
 {
     //First time to get the validation errors of this property.
     if (!errorList.ContainsKey(propertyName))
     {
         errorList[propertyName] = new List<string>();
     }
 }

The CheckErrorCollectionForProperty() method’s responsibility is to check whether the property is already registered in the error collection. If not, than it should do it, and assign an empty string list as a value, to store the list of validation errors. The GetErrors() method now returns with the list of errors associated with the property name.

Now we need ways to add and remove validation errors.

 /// <summary>
 /// Add a validation error to a property name.
 /// </summary>
 /// <param name="propertyName">Name of the property</param>
 /// <param name="error">The validation error itself</param>
 protected void AddError(string propertyName, string error)
 {
     CheckErrorCollectionForProperty(propertyName);
  
     errorList[propertyName].Add(error);
     RaiseErrorsChanged(propertyName);
 }
   
 /// <summary>
 /// Remove a specific validation error registered to a property name.
 /// </summary>
 /// <param name="propertyName">Name of the property</param>
 /// <param name="error">The validation error itself</param>
 protected void RemoveError(string propertyName, string error)
 {
     if (errorList[propertyName].Contains(error))
     {
         errorList[propertyName].Remove(error);
         RaiseErrorsChanged(propertyName);
     }
 }
  
 /// <summary>
 /// Notifies the UI of validation error state change
 /// </summary>
 /// <param name="propertyName">The name of the property that is validated</param>
 protected void RaiseErrorsChanged(string propertyName)
 {
     if (ErrorsChanged != null)
     {
         ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
     }
 }

The Add method makes sure that there is a list associated with the property name, then adds the error string to this collection. One last thing to do there, is to notify the UI, that something has changed related to the validation error of this property.

The Remove method removes the specific error from the error collection.

The infrastructure is ready by now, we can use it. The BookDetailsViewModel now inherits the ValidatingViewModelBase class.

 public class BookDetailsViewModel : ValidatingViewModelBase, IDataErrorInfo
 {
     public string Title
     {
         get
        {
             return book.Title;
         }
         set
         {
             book.Title = value;
             RaisePropertyChanged("Title");
             ValidateTitle(value);
         }
     }
  
     //Validate Title Property
     private void ValidateTitle(string title)
     {
         //Check server side data for validation result
        bookDataSource.CheckIfTitleAvailable(this.BookID, title);
     }
      
     //Callback from the server validation process
     void bookDataSource_CheckIfTitleAvailableCompleted(object sender, CheckIfTitleAvailableCompletedEventArgs e)
     {
         //Validation Logic
         if (!e.IsAvailable)
         {
             //Invalid
             AddError("Title", "The Book with that title already exits!");
         }
         else
         {
             //Valid
             RemoveError("Title", "The Book with that title already exits!");
         }
     }

In the setter of the Title property I call the ValidateTitle() method. The validate Title uses the bookDataSource, to check on the server if the title is available for this item, or another book already has it. (I made minor changes, to enable an extra WCF service operation. You can check it in the VS solution posted with this article).

Now in the completed event handler I check if the title is available. If not, then I call the previously created AddError() method. If it’s available, I call the RemoveError() method. This is happening absolutely asynchronously. The AddError() and the RemoveError() methods raise the ErrorsChanged event, so the UI gets notified also asynchronously about the change in the state of the validation.

We have to indicate on the binding, that we are using this kind of validation mechanism. This is done through the ValidatesOnNotifyDataErrors=True option

 <TextBox Grid.Column="1" TextWrapping="Wrap" d:LayoutOverrides="Height" 
          VerticalAlignment="Center" Margin="15,0" 
          Text="{Binding Title, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"/>

That’s it, you have asynchronous validation across tiers.

Bringing the UI and the ViewModels more closely

If you start to work with MVVM, you can get the taste of it very soon. You’ll really like it, but even in the beginnings there will be battles, you’ll have to fight. The problem will emerge mostly in the area of integrating the ViewModel with the specific View more closely. For example, how do you plan to display messages, or dialog boxes? What if you want to do something in the ViewModel as soon as a Storyboard ends? This is the area where behaviors, actions, and triggers can help you a lot.

For me, among many others, there are two very important and useful behavior to use with MVVM, which I’d like to introduce you now.

1. The EventToCommand behavior.

What if, I don’t want to click the load data button in my application? What if I want to load the data as soon as the application starts. (I know what is in your mind right now, please don’t ever put code in the ViewModel’s constructor that calls a real data source (WCF Service, etc…) That would make blendability a nightmare). The EvenToCommand behavior located in the MVVM Light Toolkit can help you a lot. You can attach it to a control, tell which event you are waiting for, and as soon as that event fires, the associated command object gets invoked.

 <Grid x:Name="LayoutRoot" Background="White">
     <i:Interaction.Triggers>
         <i:EventTrigger EventName="Loaded">
            <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding LoadBooksCommand}"/>
        </i:EventTrigger>
     </i:Interaction.Triggers>
As soon as the Grid’s Loaded event happens, the LoadBooksCommand gets invoked.

2. The VSMChangerBehavior

Imagine you have a special menu, panel, whatever. In some cases it’s visible, in some cases it’s not. Very easy you say, let’s create a bool property in the ViewModel, and use a converter to convert the bool value to Visibility for the UI. That definitely works. But you just tied the hands of your designer. What if his plan is to slide the panel in and out for the true and false values? What you should really do, is to define Visual States for the Visible and NonVisible states and use the VisualStateManager to move from one state to the other. Now the designer can do whatever he wants in these Visual States. What a big difference in flexibility, huh? But now you have a problem. You have a bool value, or an enum in the ViewModel. Now you have to write code on the UI, to check these states. Or let the view model “know” about the view and control it like a Controller. This is not something you want. This is where the VSMChangerBehavior created by Andras Velvart can help you out. You define states (by convention) for you enums, and let the VSMChangerBehavior do the transitions automatically, as the properties in the ViewModel change.

If you want to know more about this behavior, go and visit silverlightshow.

There are a lot of other behaviors / actions you should be familiar with, like GoToStateAction, CallMethodAction, ControlStoryboardAction, DataStateBehavior. These can help you a lot.

Final thoughts

Also there are other concepts you should consider, like I prefer to manage an IsBusy property in the ViewModel, so I have the option to indicate the user if there is something going on behind. Or for your collections you should implement paging using a PagedCollectionView class. To make your application and your components really flexible and loosely coupled, you might want to get familiar with MEF (for extensibility) or Unity (IoC container). So there is still a long way to go, but I hope these 3 articles are enough to get you started.

Today MVVM is close to be called a mature pattern. Let me rephrase it. A well supported pattern. I think if it comes to business application development, this pattern definitely should be used. Silverlight 4 is now a very good platform on which you can develop Line of Business Applications.

Download the source code


Subscribe

Comments

  • -_-

    RE: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by Bill on Jun 29, 2010 17:33
    Finally, I got my MVVM here. Thank you very much!
  • -_-

    RE: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by Lucifer Lu on Jul 12, 2010 11:46
    Excellent article, Thanks a lot really.
  • -_-

    RE: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by Wouter Janssens on Jul 28, 2010 19:04
    These three articles are very good an explained MVVM in a good way. Thank you
  • -_-

    RE: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by Todd on Jan 13, 2011 21:58
    This is really good. Thanks. How do I disable a button though...
  • -_-

    RE: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by Oscar Agreda on Mar 24, 2011 21:59

    Hello Zoltan.. great article.. very simple but at the same time very powerful and ready to be used while creating Business Apps..

    THANKS

    I was also wondering if you would ever add  part 4 maybe adding Designer data to see mock data on Blend on VS while designing the app, in the same way John Papa does in his Kung Fu MvvM implementation.

    But Zoltan MvvM Implementation is much better (I think) than the complex Kung Fu (too complex to use in the real world ).. so that is why I was wondering

  • -_-

    RE: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by zoltan.arvai on Mar 24, 2011 22:05

    Oscar... interesting idea, I'll think about it.

    Todd: RelayCommand has a constructor where you can provide a delegate for canExecute logic... however I prefer not to do so, and let the designer decide what he wants to do with the case where a command cannot be invoked... (so I use properties in the viewModel, VisualStates and behaviors to fix this problem)

  • -_-

    RE: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by MattPil29 on May 23, 2011 14:04

    Hi, I'm new to MVVM so excuse the naive question.  Is there a reason why the BooksViewModel deals directly with a Book object instead of dealing with BookDetailViewModel object i.e. the Datasource object could just retrurn detail view models insted of books.  This way the only class that touches the Book data directly is the detail view model wrapped around it. 

    Matt

  • joppe

    Re: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by joppe on Oct 13, 2011 12:05

    Very nice articles, helped a lot :)

    Thanks!

  • asdffdsa

    Re: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by asdffdsa on Feb 05, 2012 16:29

    EXCEPTIONAL KNOWLEDGE AND EXCEPTIONALLY EXPLAINED.

    THANK YOU VERY VERY MUCH

    BEAUTY is in SIMPLICITY

  • ranga_p24hotmail.com

    Re: Data Driven Applications with MVVM Part III: Validation, Bringing the UI Closer


    posted by ranga_p24hotmail.com on Mar 09, 2012 23:06

    This series is really cool. Very informative.

    Thank you for sharing. As a token of appreciation i bought the book :)

    I have one Quick request though ....Almost all sample applications code here use EF as underneath data source. ..Articles with POCO objects as DATA model will really help where database is NON-SQL Server...

     

Add Comment

Login to comment:
  *      *       

From this series