Catel 2.2: advanced validation in view models

by Geert 27. September 2011 18:34

In Catel 2.2, the validation framework has been refactored. This means that instead of this code:

   1: /// <summary>
   2: /// Validates the field values of this object. Override this method to enable
   3: /// validation of field values.
   4: /// </summary>
   5: protected override void ValidateFields()
   6: {
   7:     if (!string.IsNullOrEmpty(FirstName))
   8:     {
   9:         SetFieldError(FirstNameProperty, "First name cannot be empty");
  10:     }
  11: }

You can now write this:

   1: /// <summary>
   2: /// Validates the field values of this object. Override this method to enable
   3: /// validation of field values.
   4: /// </summary>
   5: /// <param name="validationResults">The validation results, add additional results to this list.</param>
   6: protected override void ValidateFields(List<FieldValidationResult> validationResults)
   7: {
   8:     if (!string.IsNullOrEmpty(FirstName))
   9:     {
  10:         validationResults.Add(FieldValidationResult.CreateError(FirstNameProperty, "First name cannot be empty"));
  11:     }
  12: }

 

The DataObjectBase

If you are not known with Catel, here is a little side note. There is a DataObjectBase class, which is the base class for data objects. This class implements INotifyPropertyChanged, IDataErrorInfo, and much more interfaces. The refactoring above is applied to the DataObjectBase.

Models and view models

As you probably know, it is also very easy to map properties of a model to a view model and back using the ViewModelToModel attributes. This way, you don’t have to rewrite validation that occurs in a model in the view model again. In WPF, there is an even better way using the Expose attribute:

   1: /// <summary>
   2: /// Gets or sets the person.
   3: /// </summary>
   4: [Model]
   5: [Expose("FirstName")]
   6: [Expose("MiddleName")]
   7: [Expose("LastName")]
   8: private  Person Person
   9: {
  10:     get { return GetValue<Person>(PersonProperty); }
  11:     set { SetValue(PersonProperty, value); }
  12: }
  13:  
  14: /// <summary>
  15: /// Register the Person property so it is known in the class.
  16: /// </summary>
  17: public static readonly PropertyData PersonProperty = RegisterProperty("Person", typeof(Person));

This way, the model Person is protected from the view, and only the FirstName, MiddleName and LastName are exposed to the view. Using either the ViewModelToModel or Expose attributes also takes care of validation as long as the model implements IDataErrorInfo or INotifyDataErrorInfo.

Advanced validation in view models

Now the cool thing is the combination of these two. As said before, the view model automatically validates the models and sets the error on the mapped view model properties. However, thanks to the new ValidateFields and ValidateBusinessRules methods that provide a list of errors, you can even customize the errors.

Now you might be thinking: so what? Well, think of a situation where the DAL (your entities) implement validation. You really don’t want to implement lots of different languages for the validation. In that case, you can use this method:

Setting constant validations in the DAL or validation layer

In the DAL, when an entity is validated, use something like this (pseudo-code):

   1: SetFieldError(“FirstName”, “FirstNameRequired”);

Translating validation in the view model

Then, in the view model, you can easily modify these errors:

 

   1: /// <summary>
   2: /// Validates the field values of this object. Override this method to enable
   3: /// validation of field values.
   4: /// </summary>
   5: /// <param name="validationResults">The validation results, add additional results to this list.</param>
   6: protected override void ValidateFields(List<FieldValidationResult> validationResults)
   7: {
   8:     foreach (var validationResult in validationResults)
   9:     {
  10:         if (validationResult.Message == "FirstNameRequired")
  11:         {
  12:             validationResult.Message = Properties.Resources.FirstNameRequired;
  13:         }
  14:     }
  15: }

Of course this is not something you want to actually do in your view model, so you’ll probably have to write a helper class that translates the validation for you.

 

Conclusion

You might or might not like delaying the translation of the model errors to as close as the view, but it shows how extremely powerful the improved validation of Catel is. And if you think a bit about it, wouldn’t it be a good idea to delay the translation from the server to the actual client to as close as the view…?

Tags: ,

C# | Catel | MVVM | Silverlight | Windows Phone 7 | WPF

Pingbacks and trackbacks (1)+

Comments are closed

About the Author

Geert van Horrik is an independent freelance software developer since January 1st, 2007. Since then he was been working on several projects from C++ to C# (WPF, Silverlight, ASP.NET, etc). Currently he loves to write his software using WPF (or Silverlight if WPF isn't an option).

Lately, Geert is spending a lot of time on Catel, a free open-source MVVM Framework for WPF and Silverlight. Actually, it's more than "just" an MVVM Framework, it's a complete application library!