Supporting INotifyDataErrorInfo in Universal apps

Introduktion

Windows Store app (WinRT-XAML) does not have validation build inside bindings. However, that does not mean it is not possible to get validation by a similar approach.

In this post, I am going to show how to add validation to a control and use the binding for validating.

With validation enabled on the control, it should look similar to this.

<TextBox
    Header="First name"
    Text="{Binding FirstName, Mode=TwoWay}"
    Style="{StaticResource TextBoxStyle}" />

ValidationHandler

I am utilizing the possibility to get a binding for a dependency property. For that, the framework provide me the method GetBindingEpression. The binding itself provides me with all the information needed for this to work.

Once I have the binding I can check whether the source inherits from INotifyDataErrorInfo or not.

If it does, INotifyDataErrorInfo gives me an event so I know when I should validate.

It also gives me the property value path. If it is bound to a property named FirstName I will get a string “FirstName”. Since the event gives me a string property name for the property, where the error has changed. I can now filter when to validate.

private void ControlLoaded(object sender, RoutedEventArgs e)
{
    var bindingExpression = control.GetBindingExpression(validateProperty);
    if (bindingExpression != null &&
        bindingExpression.DataItem is INotifyDataErrorInfo &&
        bindingExpression.ParentBinding != null)
    {
        dataErrorInfo = bindingExpression.DataItem as INotifyDataErrorInfo;
        propertyValuePath = bindingExpression.ParentBinding.Path.Path;

        Validate();

        dataErrorInfo.ErrorsChanged += DataErrorInfoErrorsChanged;
    }
}

I simply check if my stored property name matches the name from the event raised.

private void DataErrorInfoErrorsChanged(object sender, DataErrorsChangedEventArgs e)
{
    if (e.PropertyName == propertyValuePath)
    {
        Validate();
    }
}

In the validate method I can get the errors from INotifyDataErrorInfo. For that, I need the property name witch I already got from the binding. If it is empty the control is valid and I set the proper state and if not I set Invalid state.

private void Validate()
{
    var errorMsg = GetErrors();

    if (!string.IsNullOrEmpty(errorMsg))
    {
        SetIsInvalid();
        return;
    }

    SetIsValid();
}

Adding validation to a control

Follow these steps to add validation to any control.

  1. Setting property to validate on control.

The property to validate on the control needs to set in the ValidationHandlerFactorty.

I created a dictionary for this, set the type of the control and the dependency property as shown below.

private static readonly Dictionary<Type, DependencyProperty> propertyToValidate
    = new Dictionary<Type, DependencyProperty>
    {
       { typeof(TextBox), TextBox.TextProperty },
    };

  1. Adding validation handling to the control.

The easiest way is to add it in a style for the control.
ValidationExtensions contains an attach property called AttachValidation. Set its value to true. The ValidatoinExtensions will then attach a validation handler to the control.

<Setter
    Property="ex:ValidationExtensions.AttachValidation"
    Value="True" />

Remember to include the namespace for this to work.

xmlns:ex="using:blog.danielvistisen.com.Validation.UniApp.Extensions"
  1. Creating visual states

The validation handler will try to set the following states on the control

  • Valid
  • Invalid
<VisualStateGroup
    x:Name="ValidationStates">
    <VisualState
        x:Name="Invalid" />
    <VisualState
        x:Name="Valid" />
</VisualStateGroup>

In the demo project, I change the border color to red in the invalid state to keep the code simple. You can of course chose a more fitting layout for your app.

Would like to hear your thoughts on this approach so feel free to leave a comment below. Questions is most welcome as well. Happy coding.

You can download the demo project here.

P.S Make sure you follow me on twitter @danielvistisen for updates on new posts.

Supporting INotifyDataErrorInfo in Universal apps