Simple validation in WPF MvvM Part 2

By Mirek on (tags: IDataErrorInfo, mvvm, validation, WPF, categories: code)

In a previous post I showed you how to use IDataErrorInfo interface to implement simple validation mechanism in WPF MvvM application. In this post we will try to extend and customize this approach a little.

It would be nice I the fields of the form be validated on Save button click. So the user will not see error messages when he opens the for for the first time

Clipboard02

To achieve that we have to change the approach a little bit. Since IDataErrorInfo raises the validation every time the property source is changed, we can control it by introducing IsValidation boolean flag which gets true when model is being validated. Let’s see the code of the ProductModel

   1: public class ProductModel : NotifyPropertyChangeBase , IDataErrorInfo
   2:  {
   3:      public bool IsValidating = false;
   4:  
   5:      public Dictionary<string, string> Errors = new Dictionary<string, string>();
   6:  
   7:      ...properties...
   8:  
   9:      public bool IsValid()
  10:      {
  11:          IsValidating = true;
  12:          try
  13:          {
  14:              NotifyPropertyChanged(() => Name);
  15:              NotifyPropertyChanged(() => Price);
  16:              NotifyPropertyChanged(() => Amount);
  17:          }
  18:          finally
  19:          {
  20:              IsValidating = false;
  21:          }
  22:          return (Errors.Count() == 0);
  23:      }
  24:  
  25:      public string Error
  26:      {
  27:          get { throw new NotImplementedException(); }
  28:      }
  29:  
  30:      public string this[string columnName]
  31:      {
  32:          get
  33:          {
  34:              string result = string.Empty;
  35:              if (!IsValidating) return result;
  36:              Errors.Remove(columnName);
  37:              switch (columnName)
  38:              {
  39:                  case "Name": if (string.IsNullOrEmpty(Name)) result = "Name is required!"; break;
  40:                  case "Price": if ((Price < 10) || (Price > 1000)) result = "Price must be between 10 and 1000"; break;
  41:                  case "Amount": if ((Amount < 1) || (Amount > 100)) result = "Amount must be between 1 and 100"; break;
  42:              };
  43:              if (result != string.Empty) Errors.Add(columnName, result);
  44:              return result;
  45:          }
  46:      }
  47:  
  48:      public ProductModel()
  49:      {
  50:      }
  51:  }

As you can see at line 35 the data validation is performed only when IsValidating flag is set to true. New method IsValid() returns true if there are no data errors on the model. The validation flag is set to true here and all properties are notified as changed, lines 14-16. This is required for the framework to invoke data validation and inform binded controls about validation errors. As you can see, each validation error is stored in additional dictionary Errors accessed by property name. This dictionary is used to indicate if there are any errors in the model and to return proper value in IsValid() method.
Now, since UpdateSourceTrigger  parameters are set to its default for all editable text boxes in the form, which is LostFocus (dafault for TextBoxes) then all values are updated on the model, but since the IsValidating flag is not set then no errors are showed.

In save command of save button

   1: private void SaveExecute()
   2: {
   3:     Model.IsValid();
   4: }

the properties are updated again with validation flag set to true. The data is validated and errors are displayed on the form.