August 8th, 2017

Validation in Xamarin.Forms Enterprise Apps

animationWe released an eBook this past May on Enterprise Application Patterns using Xamarin.Forms. The eBook focuses on core patterns and architectural guidance for developing Xamarin.Forms enterprise apps that are easier to test, maintain, and evolve. Guidance is provided on how to implement the Model-View-ViewModel (MVVM) pattern, dependency injection, navigation, validation, and configuration management, while maintaining loose coupling. In addition, there’s also guidance on performing authentication and authorization with IdentityServer, accessing remote data from containerized microservices, and unit testing.

This blog post explores validation in Xamarin.Forms enterprise apps. There are, of course, many approaches that can be taken to validation. What’s presented here is the validation approach taken in the eShopOnContainers mobile app, which is extensible, easily unit testable, and supports data binding and property change notification.

Introduction to Validation

Any app that accepts input from users should ensure that the input is valid. An app could, for example, check for input that contains only characters in a particular range, is of a certain length, or matches a particular format. Without validation, a user can supply data that causes the app to fail. Validation enforces business rules and prevents an attacker from injecting malicious data.

In the context of the Model-ViewModel-Model (MVVM) pattern, a view model or model will often be required to perform data validation and signal any validation errors to the view so that the user can correct them. The eShopOnContainers mobile app performs synchronous client-side validation of view model properties and notifies the user of any validation errors by highlighting the control that contains the invalid data, and by displaying error messages that inform the user of why the data is invalid. The following diagram shows the classes involved in performing validation in the eShopOnContainers mobile app:

View model properties that require validation are of type ValidatableObject<T>, and each ValidatableObject<T> instance has validation rules added to its Validations property. Validation is invoked from the view model by calling the Validate method of the ValidatableObject<T> instance, which retrieves the validation rules and executes them against the ValidatableObject<T> Value property. Any validation errors are placed into the Errors property of the ValidatableObject<T> instance, and the IsValid property of the ValidatableObject<T> instance is updated to indicate whether validation succeeded or failed.

Property change notification is provided by the ExtendedBindableObject class, and so an Entry control can bind to the IsValid property of the ValidatableObject<T> instance in the view model class to be notified of whether or not the entered data is valid.

Specifying Validation Rules

Validation rules are specified by creating a class that derives from the IValidationRule<T> interface, which is shown in the following code example:

public interface IValidationRule<T>
{
    string ValidationMessage { get; set; }
    bool Check(T value);
}

This interface specifies that a validation rule class must provide a boolean Check method that is used to perform the required validation, and a ValidationMessage property whose value is the validation error message that will be displayed if validation fails.

The following code example shows the IsNotNullOrEmptyRule<T> validation rule, which is used to perform validation of the username and password entered by the user on the LoginView when the eShopOnContainers mobile app is configured to use mock services:

public class IsNotNullOrEmptyRule<T> : IValidationRule<T>
{
    public string ValidationMessage { get; set; }

    public bool Check(T value)
    {
        if (value == null)
        {
            return false;
        }

        var str = value as string;
        return !string.IsNullOrWhiteSpace(str);
    }
}

The Check method returns a boolean indicating whether the value argument is null, empty, or consists only of whitespace characters.

Adding Validation Rules to a Property

In the eShopOnContainers mobile app, view model properties that require validation are declared to be of type ValidatableObject<T>, where T is the type of the data to be validated. The following code example shows an example of one such property:

public ValidatableObject<string> UserName
{
    get
    {
        return _userName;
    }
    set
    {
        _userName = value;
        RaisePropertyChanged(() => UserName);
    }
}

For validation to occur, validation rules must be added to the Validations collection of the ValidatableObject<T> instance, as demonstrated in the following code example:

private void AddValidations()
{
    _userName.Validations.Add(new IsNotNullOrEmptyRule<string> 
    { 
        ValidationMessage = "A username is required." 
    });
}

This method adds the IsNotNullOrEmptyRule<T> validation rule to the Validations collection of the ValidatableObject<T> instance, including a value for the ValidationMessage property, which specifies the validation error message that will be displayed if validation fails.

Triggering Validation

Validation can be triggered manually for a view model property. For example, this occurs in the eShopOnContainers mobile app when the user taps the Login button on the LoginView, when using mock services. The command delegate calls the MockSignInAsync method in the LoginViewModel, which invokes validation by executing the Validate method, which in turn invokes the ValidateUserName method:

private bool ValidateUserName()
{
    return _userName.Validate();
}

The ValidateUserName method performs validation of the username entered by the user on the LoginView, by invoking the Validate method on the ValidatableObject<T> instance. The following code example shows the Validate method from the ValidatableObject<T> class:

public bool Validate()
{
    Errors.Clear();

    IEnumerable<string> errors = _validations
        .Where(v => !v.Check(Value))
        .Select(v => v.ValidationMessage);

    Errors = errors.ToList();
    IsValid = !Errors.Any();

    return this.IsValid;
}

This method clears the Errors collection, and then retrieves any validation rules that were added to the object’s Validations collection. The Check method for each retrieved validation rule is executed, and the ValidationMessage property value for any validation rule that fails to validate the data is added to the Errors collection of the ValidatableObject<T> instance. Finally, the IsValid property is set, and its value is returned to the calling method, indicating whether validation succeeded or failed.

Validation is also automatically triggered whenever a bound property changes. For more information, see Triggering Validation when Properties Change.

Displaying Validation Errors

The eShopOnContainers mobile app notifies the user of any validation errors by highlighting the control that contains the invalid data with a red line, and by displaying an error message that informs the user why the data is invalid below the control containing the invalid data. The following screenshot shows part of the LoginView in the eShopOnContainers mobile app when a validation error is present:

Highlighting a Control that Contains Invalid Data

The LineColorBehavior attached behavior is used to highlight Entry controls where validation errors have occurred. The following code example shows how the LineColorBehavior attached behavior is attached to an Entry control:

<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
    <Entry.Style>
        <OnPlatform x:TypeArguments="Style"
          iOS="{StaticResource EntryStyle}"
          Android="{StaticResource EntryStyle}"
          WinPhone="{StaticResource UwpEntryStyle}"/>
    </Entry.Style>
    ...
</Entry>

The Entry control consumes an explicit style, which is shown in the following code example:

<Style x:Key="EntryStyle"
       TargetType="{x:Type Entry}">
    ...
    <Setter Property="behaviors:LineColorBehavior.ApplyLineColor"
            Value="True" />
    <Setter Property="behaviors:LineColorBehavior.LineColor"
            Value="{StaticResource BlackColor}" />
    ...
</Style>

This style sets the ApplyLineColor and LineColor attached properties of the LineColorBehavior attached behavior on the Entry control. When the value of the ApplyLineColor attached property is set, or changes, the LineColorBehavior attached behavior executes the OnApplyLineColorChanged method, which adds or removes the EntryLineColorEffect class to the Entry‘s Effects collection. For more information about the EntryLineColorEffect class, see Highlighting a Control that Contains Invalid Data.

The Entry control also has a DataTrigger added to its Triggers collection. The following code example shows the DataTrigger:

<Entry Text="{Binding UserName.Value, Mode=TwoWay}">
    ...
    <Entry.Triggers>
        <DataTrigger 
            TargetType="Entry"
            Binding="{Binding UserName.IsValid}"
            Value="False">
            <Setter Property="behaviors:LineColorBehavior.LineColor" 
                    Value="{StaticResource ErrorColor}" />
        </DataTrigger>
    </Entry.Triggers>
</Entry>

This DataTrigger monitors the UserName.IsValid property, and if it’s value becomes false, it executes the Setter, which changes the LineColor attached property of the LineColorBehavior attached behavior to red.

Displaying Error Messages

The UI displays validation error messages in Label controls below each control whose data failed validation. The following code example shows the Label that displays a validation error message if the user has not entered a valid username:

<Label Text="{Binding UserName.Errors, Converter={StaticResource FirstValidationErrorConverter}"
       Style="{StaticResource ValidationErrorLabelStyle}" />

Each Label binds to the Errors property of the view model object that’s being validated. The Errors property is provided by the ValidatableObject<T> class, and is of type List<string>. Because the Errors property can contain multiple validation errors, the FirstValidationErrorConverter instance is used to retrieve the first error from the collection for display.

Summary

Enterprise Application Patterns using Xamarin.Forms focuses on core patterns and architectural guidance for developing Xamarin.Forms Enterprise Apps that are easier to test, maintain, and evolve. The eBook also ships with a sample application, the eShopOnContainers mobile app, which performs synchronous client-side validation of view model properties and notifies the user of any validation errors by highlighting the control that contains the invalid data, and by displaying error messages that inform the user why the data is invalid.

Author

0 comments

Discussion are closed.