In my previous article about validation in MVVM and PCL, I discussed what functions in .NET framework is available to use, when targeting both Windows 8 and Windows Phone 8. How to do validation in MVVM with PCL (1)

In this article, I will discuss how to trigger the validations.

The first thing is to choose between Property validation and Batch validation. Property validation triggers a validation every time a property in the view is changed. (It could also be triggered on every keystroke or touch event.) Batch validation triggers when all fields are populated and the view should do something with that information, such as saving it. What you should use is dependent on many factors, including what kind of data is it; how is the user interface able to give feedback on errors.

I will use both Property and Batch validation in my example.

Property Validation

Property validation can easily be implemented in the property setter of the view.

public class MainViewModel : ViewModelBase
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set {
            if (string.IsNullOrEmpty(value))
            {
                AddError("FirstName", "Can't be null");
            }
            else
            {
                _firstName = value;
                NotifyPropertyChanged();
            }
        }
    }
}

Here we write our rules inline in the setter and we add an error into the error collection if the validation is not successful. (The AddError method will we discuss in a later article.)

I do not like this solution mainly because of two things:

  • I want all my validation rules to be defined in one place.
  • I want to have setters that does not include so much code.

Why do you not use DataAnnotations? As I described in my previous article, DataAnnotations is not implemented in Windows Phone. I can use it in the Windows 8 view project, but then I will have my rules spread all over the code.

We can of course refactor this code and create one method that is called from the setter. However, we can use an event that we already are dependent on in the view, PropertyChanged.

In views constructor I subscribe on PropertyChanged and do all property validation in the method validate.

public MainViewModel()
{
    PropertyChanged += validate;
}
private void validate(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    validate(e.PropertyName);
}

private void validate(string propertyName)
{
    switch (propertyName)
    {
        case "FirstName":
            AddErrors(propertyName, Person.ValidateFirstName(FirstName));
            break;
        case "LastName":
            AddErrors(propertyName, Person.ValidateLastName(LastName));
            break;
    }
}

I store all my validation rules as static methods on the model class, in this case Person. (More about this in the article about where to store validation rules.)

My property setter / getter is much cleaner.

public class MainViewModel : ViewModelBase
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; NotifyPropertyChanged(); }
    }
}

Batch Validation

I also implement validations when I do some action on the information in the view, for example when I save a new Person.

private void executeSaveCommand()
{
    Person person = new Person() { FirstName = this.FirstName, LastName = this.LastName };
    var errorsFromModel = Person.Validate(person);
    if (errorsFromModel != null)
    {
        // Some error is reported
        _errors = errorsFromModel;
        NotifyPropertyChanged("Errors");
    }
}

In this case I first instantiate a new model class (Person) and then call the method Validate on that class.

I implement the Validate method in the model class.

public static Dictionary<string, List<string>> Validate(Person person)
{
    Dictionary<string, List<string>> dictionaryErrors = new Dictionary<string, List<string>>();
    addErrorToDictionary("FirstName", ValidateFirstName(person.FirstName), dictionaryErrors);
    addErrorToDictionary("LastName", ValidateFirstName(person.LastName), dictionaryErrors);

    return dictionaryErrors;
}

I use the same statics methods to do the validation.