Thursday, 14 March 2013

Posted by Prasad KM | 21:29 Categories:

WPF Validation with IDataErrorInfo  and INotifyDataErrorInfo

In this post I’ll describe how to use the IDataErrorInfo and INotifyDataErrorInfo interfaces to perform validation in WPF and Silverlight.
There are several kinds of validations and the preferrable approach in the MVVM pattern is using the IDataErrorInfo or INotifyDataErrorInfo interface.
But this approach doesn’t behave as expected: it performs validation immediately after the launch of the view, whereas it would be better if the validation started only after a user has changed values. Also I would like to reuse some commonly used rules.
So I’ve created the helper class which performs all the necessary work. This class is similar to the class from the FluentValidation library, but it has less code and more flexible to me.
Here is the example of such validation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class PersonViewModel : IDataErrorInfo, INotifyPropertyChanged
{
    private ModelValidator _validator = new ModelValidator();
    public PersonViewModel()
    {
        //Six validation rules
        this._validator.AddValidationFor(() => this.FirstName).NotEmpty()
            .Show("Enter a First Name");
        this._validator.AddValidationFor(() => this.LastName).NotEmpty()
            .Show("Enter a Last Name");
        this._validator.AddValidationFor(() => this.Age).NotEmpty()
            .Show("Enter an age");
        this._validator.AddValidationFor(() => this.Age).Between(0,99)
            .Show("The age must be between 0 and 99");
        this._validator.AddValidationFor(() => this.Email)
            .When(() => this.IsSubscribe).NotEmpty()
            .Show("Enter an email address");
        this._validator.AddValidationFor(() => this.Email)
            .When(() => this.IsSubscribe).EmailAddress()
            .Show("Enter a valid email address");
        //Validates on changes of properties
        this.PropertyChanged += (s, e) => this._validator.ValidateProperty(e.PropertyName);
    }
    public string Error
    {
        get { throw new NotImplementedException(); }
    }
    public string this[string columnName]
    {
        get { return this._validator.GetErrors(columnName).FirstOrDefault(); }
    }

There is only 4 steps:

1. Create a variable of the ModelValidator type
2. Add necessary validation rules
3. Update validation every time when a property has been changed
4. Return cached errors inside the indexer property.
Actually it is not so obligatory to implement the 3rd step and update validation inside the PropertyChanged event handler. You can remove that line and call the ValidateAll method after a user clicks the Save button. But validation on changes is more preferrable solution.

I’ve added some predefined validation rules, if they aren’t enough, you can use the Must method with your own functions.
1. NotNull – the property value shouldn’t be null.
2. NotEmpty – if the property is of the string type, checks whether the string is null or consists of whitespaces. If of any other type – this rule is the same as NotNull.
3. Between – checks whether the property value is within a specific range. Works with strings as well as with numbers, so it’s not necessary to parse strings in view models.
4. EmailAddress – if the string value isn’t null, it should be a valid e-mail address.
5. Must – any user-defined validation function. For example: this._validator.AddValidationFor(() => this.LastName).Must(() => this.LastName.Length < 10).Show("Last name should not exceed 10 characters");

0 comments:

  • RSS
  • Delicious
  • Digg
  • Facebook
  • Twitter
  • Linkedin
  • Youtube