ValidatableBase 2.0

A couple of weeks ago I released verison 2.0 of ValidatableBase on GitHub. The newest build provides Data Annotation styled attribute validation for Universal Windows applications, iOS, Android and any other platform that .NET Portable Class Libraries can run on.

Release features

  • New Data Annotation styled attribute validation system has been added. The attributes implement IValidationRule and provide a simple means of writing custom validation rules if needed.
  • Registration of properties is no longer needed.
  • The library is now in a Portable Class Library, usable across all platforms that PCL's can run on.
    • As a side-effect of this, the validation messages collection is no longer directly exposed. The underlying implementation is Dictionary< string, IEnumerable< IValidationMessage>> which requires conversion if you need to bind to the validation results. The example app includes an extension method that can be used to perform the conversion, for view models that want to expose validation to the view.
  • ValidatableBase now has a ValidationChanged event that you can subscribe to. Any time validation is changed, either by adding/removing messages or running validation, your view models can be notified.

Validation Rule examples

ValidatableBase supports a very flexible attribute based validation system that can be used on any of your models, regardless of platform.

To demonstrate, let's create a very basic model with zero validation.

namespace SampleUniversalApp.Models
{
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    using Sullinger.ValidatableBase.Models;
    using Sullinger.ValidatableBase.Models.ValidationRules;

    public class User : INotifyPropertyChanged
    {
        private string email = string.Empty;

        private string password = string.Empty;

        public event PropertyChangedEventHandler PropertyChanged;

        public string Email
        {
            get
            {
                return this.email;
            }

            set
            {
                this.email = value;
                this.OnPropertyChanged("Email");
            }
        }

        public string Password
        {
            get
            {
                return this.password;
            }

            set
            {
                this.password = value;
                this.OnPropertyChanged("Password");
            }
        }

        protected virtual void OnPropertyChanged(string propertyName = "")
        {
            var handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

This model is really straight-forward and shouldn't need any explaining. It implements INotifyPropertyChanged and has two properties.

Validation

Now that we have a model, we want to validate it. We will perform validation on both the Email and the Password properties. We want to make sure neither of them are blank.

The first thing we have to do is inherit from a concrete Type implementing IValidatable. The ValidatableBase API ships with a base class called ValidatableBase (surprise, surprise) that you can use.

Update your class definition to look like the following:

public class User : ValidatableBase, INotifyPropertyChanged

Since the ValidatableBase API is cross-platform and lives within a Portable Class Library, the ValidatableBase class does not implement INotifyPropertyChanged. If you are targeting a platform that supports this (such as WPF) then you can build a secondary base-class on top of ValidatableBase, or build your own implementation of IValidatable.

The ValidatableBase API performs validation mostly through attributes. To make sure neither property is left blank, we will decorate each property with a ValidateObjectHasValue attribute.

    [ValidateObjectHasValue(
        FailureMessage = "E-Mail can not be left blank.", 
        ValidationMessageType = typeof(ValidationErrorMessage))]
    public string Email
    {
        get
        {
            return this.email;
        }

        set
        {
            this.email = value;
            this.OnPropertyChanged("Email");
        }
    }

    [ValidateObjectHasValue(
        FailureMessage = "Password can not be left blank.",
        ValidationMessageType = typeof(ValidationErrorMessage))]
    public string Password
    {
        get
        {
            return this.password;
        }

        set
        {
            this.password = value;
            this.OnPropertyChanged("Password");
        }
    }

Performing the validation

The next step is to obviously perform the validation. To do that, we will create a view model that implements ICommand, exposing an Execute method.

public class MainPageViewModel : ICommand, INotifyPropertyChanged
{
    public MainPageViewModel()
    {
        this.AppUser = new User();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public event EventHandler CanExecuteChanged;
    public User AppUser { get; private set; }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        // Perform validation on the user's built in validation.
        this.AppUser.ValidateAll();

        // Do stuff.
        return;
    }

We have now validated our model. You can check if the model has any validation messages (indicating failure) by using the HasValidationMessages< IValidationMessage>() method.

if (this.AppUser.HasValidationMessages< ValidationErrorMessage>())
{
    // Failed, so abort.
    return;
}

Adding additional validation rules.

Since the models validation is performed by the attributes, we can come back at a later date and add or replace validation on the properties without having to re-write a large amount of logic code. To demonstrate, we will replace the Password properties validation. Instead of checking if the property is empty, we will check if the password entered meets a required password range of 6 to 20 characters.

    [ValidateStringIsGreaterThan(GreaterThanValue = 6, 
        FailureMessage = "Password must be greater than 6 characters.", 
        ValidationMessageType = typeof(ValidationErrorMessage))]
    [ValidateStringIsLessThan(LessThanValue = 20, 
        FailureMessage = "Password must be less than 20 characters.", 
        ValidationMessageType = typeof(ValidationErrorMessage))]
    public string Password
    {
        get
        {
            return this.password;
        }

        set
        {
            this.password = value;
            this.OnPropertyChanged("Password");
        }
    }

Now when we run the this.AppUser.ValidateAll() method, the property will ensure it has a password between the range of 6 characters and 20. If not, a ValidationErrorMessage is created.