Monthly Archives: October 2017

Data validation in UWP

In this article we describe how to implement validation of an object –typically an Entity, a Model, or a ViewModel instance- in a UWP app. We’ll use the Template10 Validation core classes for this.

The validation of an instance includes:

  • validating individual fields,
  • comparing two (or more) fields to each other,
  • comparing a newly assigned value to its original value,
  • verifying whether one or more fields have changed, and
  • undoing all changes to the instance.

I built a small sample app to illustrate all of this. Here’s how the main page looks like: on the left there’s a list of company cars. If one of these is selected, its details appear in the middle and we show an Edit button in the top right corner:

List

When the Edit button is clicked, an edit form pops open inside a content dialog. All fields of the ViewModel are editable. At the bottom of the dialog there are some switches that indicate the state of the instance (whether it was changed in the dialog, and whether it is valid) and a button to reset all fields to their original value.

If the object breaks any of the validation rules, then a list of errors appears at the bottom. As long as there are validation errors, the Save button is disabled:

Screenshot

I think this is a quite common use case, especially in enterprise environments. Here’s how I built it.

Template 10 Validation

The Template 10 Validation project by Jerry Nixon covers all of these scenarios with Property<T> and ValidationModelBase as its core classes. I copied these classes with their helpers and interfaces into my own project (under Services/Validation):

classes

I know it would have been easier to just reference the Template 10 Validation NuGet package. But I initially wanted to identify the smallest possible set of classes that cover my requirements and then make an old school Portable Class Library or a more modern .NET Standard Library from it. Unfortunately that was too ambitious. The current implementation relies on IObservableMap<K,V> from the Windows.Foundations.Collection namespace which –as its name implies- is not platform independent. If ObservableDictionary<K,V> could be rewritten without members from this namespace, then I think it would be possible to create a .NET Standard version of the code, that could then be used in environments such as Xamarin…

I kept all the code as is, except for the JsonIgnore and NotMapped attributes on some properties. These were removed to further reduce dependencies. Here’s an overview of the seven classes and interfaces that make up the Validation service:

ValidationClassDiagram

The core class is ValidatableModelBase, which holds the default implementation for an editable data object that supports validation:

  • an implementation of INotifyPropertyChanged,
  • an internal collection of Property<T> instances that store the new and original values of all its properties,
  • a Validator Action<T> delegate to hold your validation logic,
  • a Validate method to trigger the validation,
  • an Errors collection to hold the validation messages,
  • IsValid and isDirty properties to inspect the state, and
  • a Revert method to undo all changes.

Let’s see how this works in practice.

A simple Model

My UWP sample app contains the following class to hold Company Car information. Instances of this Model class are bound to the list at the left, and to the read-only details. The Model class uses the change propagation that comes with my own compact MVVM library and I will stick to this:

public class CompanyCar : BindableBase
{
    private string _brand;
    private string _type;
    private string _body;
    // ...

    public string Brand
    {
        get { return _brand; }
        set { SetProperty(ref _brand, value); }
    }

    public string Type
    {
        get { return _type; }
        set { SetProperty(ref _type, value); }
    }

    public string Body
    {
        get
        {
            return _body;
        }
        set
        {
            SetProperty(ref _body, value);
            OnPropertyChanged(nameof(BodyIcon));
        }
    }

    // ...
}

An editable and validatable ViewModel

To create the corresponding ViewModel class that will be bound to the edit dialog, you must plug ValidatableModelBase somewhere in its inheritance chain and define the properties in such a way that they are automatically hooked to the underlying Property<T> instances:

public class CompanyCarViewModel : ValidatableModelBase
{
    public string Brand { get { return Read<string>(); } set { Write(value); } }

    public DateTime ProductionDate { get { return Read<DateTime>(); } set { Write(value); } }

    public double Mileage { get { return Read<double>(); } set { Write(value); } }

    // ...
}

Of course you also need to provide the validation logic. The validation delegate typically iterates through a set of rules. Any validation rule that is not met by the instance, adds a message in the Errors collection of the corresponding property. Here are some validation rules from the sample app:

  • Brand and Type are mandatory
  • Date of first use should come after date of production
  • Mileage may not decrease

Validations on a single field can be done in many ways with many frameworks, most of which rely on data annotation through validation attributes on the properties. Comparing two fields in a validation rule is a lot more complex (but not impossible, there’s an example of using Prism for this right here). I am not aware of a library that allows to compare the changed value of a field to its original value, except for the bloated self-tracking entities. Nevertheless this is a requirement that I often encounter (typically with counters and dates). I believe that the Template 10 Validation classes provide an excellent service in an elegant way and with minimal impact on your app.

Here’s the code for the validation rules in the sample app:

private void Validation_Executed(CompanyCarViewModel c)
{
    if (string.IsNullOrEmpty(c.Brand))
    {
        c.Properties[nameof(c.Brand)].Errors.Add("Brand is mandatory.");
    }

    if (string.IsNullOrEmpty(c.Type))
    {
        c.Properties[nameof(c.Type)].Errors.Add("Type is mandatory.");
    }

    // Compare two properties.
    if (c.FirstUseDate < c.ProductionDate)
    {
        // Unfortunately errors have to be assigned to a property.
        c.Properties[nameof(c.FirstUseDate)].Errors.Add("Date of first use should come after date of production.");
    }

    // Compare with original value.
    if (c.Mileage < (c.Properties[nameof(c.Mileage)] as Property<double>).OriginalValue)
    {
        c.Properties[nameof(c.Mileage)].Errors.Add("Turning back the mileage is illegal.");
    }
}

The ViewModel is ready now, so let’s provide edit functionality in its most common form: as a modal edit dialog.

The Edit Dialog

According to the dialogs and flyouts recommendations, a ContentDialog would be the ideal control for this in UWP. If you’re planning to use content dialogs for more than just messages and confirmation, then you would want to override some of the size restrictions in the default style. Here’s how to set the maximum width from the default of 548 pixels (which is way to small for an edit form) to 800, in app.xaml:

<x:Double x:Key="ContentDialogMaxWidth">800</x:Double>

In editable fields, don’t forget to set the BindingMode to TwoWay (it’s not the default):

<TextBox Header="Brand" Text="{Binding Brand, Mode=TwoWay}" />

Here’s the XAML for the status bar at the bottom, displaying the validation errors:

<ItemsControl ItemsSource="{Binding Errors, Mode=OneWay}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

And here’s how the entire edit form looks like in action:

Screenshot

When the Edit button is clicked, you need to create the ViewModel from the currently selected Model. You can use any kind of Mapper for this, or conversion operators, or you can do it in a constructor like this:

public CompanyCarViewModel(Models.CompanyCar model)
{
    this.Brand = model.Brand;
    this.Type = model.Type;
    this.Body = model.Body;
    this.ProductionDate = model.ProductionDate;
    this.FirstUseDate = model.FirstUseDate;
    this.PowerUnit = model.PowerUnit;
    this.Emission = model.Emission;
    this.Mileage = model.Mileage;
    this.Driver = model.Driver;
    this.Validator = that => { Validation_Executed(that as CompanyCarViewModel); };

    // Not needed: ValidatableModelBase constructor does this.
    // this.PropertyChanged += (o, e) => Val(o as CompanyCar);
}

Of course you also need to provide the code to update the Model with the –validated- data when the Save button was pressed:

internal void Update(Models.CompanyCar model)
{
    model.Brand = this.Brand;
    model.Type = this.Type;
    model.Body = this.Body;
    model.ProductionDate = this.ProductionDate;
    model.FirstUseDate = this.FirstUseDate;
    model.PowerUnit = this.PowerUnit;
    model.Emission = this.Emission;
    model.Mileage = this.Mileage;
    model.Driver = this.Driver;
}

Here’s the code to open the dialog and to make sure that the primary button is disabled as long as there are validation errors:

// Prepare ViewModel.
var companyCarViewModel = new CompanyCarViewModel(ViewModel.SelectedCompanyCar);
companyCarViewModel.PropertyChanged += (obj, ev) => EditDialog.IsPrimaryButtonEnabled = companyCarViewModel.IsValid;
companyCarViewModel.Validate();
EditDialog.DataContext = companyCarViewModel;

// Process Dialog.
var result = await EditDialog.ShowAsync();

Here’s how the Model is updated when the dialog is closed through the primary button:

var result = await EditDialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
    // Update model.
    companyCarViewModel.Update(ViewModel.SelectedCompanyCar);
}

Here’s how to reset the ViewModel to its original values:

private void ResetButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    (EditDialog.DataContext as CompanyCarViewModel).Revert();
}

Wait, there’s more

Validation checks are not restricted to edit forms. You could for example validate all the (view)models in the UI, to show your end users which objects need some attention. Just bind the IsValid property to an element in the data template, like in this example from the Template10 Validation sample app:

Template10ValidationMain

That sample project also shows how to decorate the controls in the edit form with error indicators, like this:

Template10Validation

This requires some extra behaviors and control templates from the NuGet package. I don’t have these in my own sample app, because I was looking for reusable logic, not UI.

The code

My own sample app lives here on GitHub.

Enjoy!

Advertisements