Catel: Hooking a command to validation automatically in MVVM

by Geert 20. December 2011 19:42

In Catel (2.5, not released yet, only beta available), it is possible to hook the CanExecute of a Command to the IValidationSummary automatically. This way, there is no need to check for errors manually in the CanExecute. The example below first adds a validation summary to a view model to get the validation result. Then, it uses this validation summary to automatically determine whether a command can be executed.

1. Add validation to a person view model (note how the validation adds the tag PersonValidation to a validation):

   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(System.Collections.Generic.List<IFieldValidationResult> validationResults)
   7: {
   8:     if (string.IsNullOrEmpty(FirstName))
   9:     {
  10:         validationResults.Add(FieldValidationResult.CreateError(FirstNameProperty, "First name cannot be empty", "PersonValidation"));
  11:     }
  12:  
  13:     if (string.IsNullOrEmpty(LastName))
  14:     {
  15:         validationResults.Add(FieldValidationResult.CreateError(LastNameProperty, "Last name cannot be empty", "PersonValidation"));
  16:     }
  17: }

2. Add a property to the view model containing the validation summary using the ValidationToViewModel attribute.

   1: [ValidationToViewModel(Tag = "PersonValidation")]
   2: public IValidationSummary PersonValidationSummary { get; set; }

3. Define a command on the view model:

   1: /// <summary>
   2: /// Gets the Save command.
   3: /// </summary>
   4: public Command Save { get; private set; }
   5:  
   6: /// <summary>
   7: /// Method to invoke when the Save command is executed.
   8: /// </summary>
   9: private void OnSaveExecute()
  10: {
  11:     // TODO: Handle command logic here
  12: }

4. Create the command that automatically uses the validation summary using the CommandHelper class:

   1: Save = CommandHelper.CreateCommand(OnSaveExecute, () => PersonValidationSummary);

With this example, the Save command on the view model can only be executed when there are no errors with the PersonValidation tag.

Tags: ,

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

Catel: improved validation and the ValidationContext

by Geert 2. December 2011 12:20

In every release of Catel, we try to improve a significant part of the code based on user requests. For release 2.4, it was the turn of the validation.

The validation in Catel is extremely flexible, and allows to use validation in the model (exposed via INotifyDataErrorInfo or IDataErrorInfo), validation in the view model (via the Validate methods) or via attributes (data annotations) on either the model or view model. However, this doesn’t seem enough for some users that require the absolute freedom in validation. Therefore, after intense discussions with the users, we came up with the following changes.

Introduction of IValidator and IValidatorProvider

The first request was to allow external validators to get involved into the validation process. For example, some use Fluent Validation for advanced validation checks. In previous versions of Catel, the calls to the external validators had to be implemented manually and converted to IValidationResult interfaces.

To make it much easier to hook into the validation process, the IValidatorProvider and IValidator were developed. The IValidatorProvider is a class that returns the right IValidator for a specific type. If no validator is available, it will return null. The ViewModelBase will automatically check the ServiceLocator whether an IValidatorProvider is available, and will then retrieve the IValidator for the current instance automatically.

The IValidator itself contains lots of methods, and allows to hook into the following events:

  • BeforeValidation
  • BeforeFieldValidation
  • FieldValidation
  • AfterFieldValidation
  • BeforeBusinessRuleValidation
  • BusinessRuleValidation
  • AfterBusinessRuleValidation
  • AfterValidation

For example, it can use Fluent Validation in the FieldValidation  and BusinessRuleValidation, and can translate the errors in AfterValidation.

Introduction of IValidationContext

Another nice improvement is the creation of the IValidationContext. It takes over the responsibility as validation container from the DataObjectBase. This way, the DataObjectBase becomes a bit less complicated and at the same time, the new container allows querying of all validations of a specific object in a very flexible way. Below are a few examples on how to query validations of a specific object:

First, the ValidationContext can be retrieved from a view model like this:

   1: var viewModel = new MyViewModel();
   2: var validationContext = viewModel.ValidationContext;

Get all field errors with a specific tag

   1: var fieldErrorsWithTag = validationContext.GetFieldErrors((object)"myTag");

Get all field validations of a specific property

   1: var fieldValidationResultsForProperty = validationContext.GetFieldValidationResults("MyProperty");

Get all business rule warnings

   1: var businessRuleWarnings = validationContext.GetBusinessRuleWarnings();

We hope to release Catel 2.4 this weekend (waiting on the last feedback), so you don’t have to wait long for these features Glimlach.

Tags: ,

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

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


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!