WPF Input Validation Part 1: Tutorial and examples

I’ve recently set up a sample of a validation mechanism for WPF text input controls on the UI that I thought it would be of general interest due to the “Gokuu approach”* I’ve done with it, otherwise it just wouldn’t make my day :) The requirement is to create a UI control behavior that visually signals validation errors over user input, so when the user submits empty or invalid data on a mandatory field, the input control changes it’s appearance or some kind of action takes place, like a validation message box. Then, it should prevent data submission when a button is clicked or the Enter key is pressed, for instance.

I’m talking about WPF Validation Rules. Validation Rules are objects that apply validation over a data Binding operation, and they are added to the Binding.ValidationRules Collection. They can be created by deriving from the ValidationRule class and overriding its Validate method that should hold the validation logic. Then, normally, you would add the rule to the collection in your xaml code, through property element syntax, or in code.

Although not a part of this article, there’s another validation mechanism built into WPF. You can also test one’s input through an ExceptionValidationRule, which invalidates a value if there are exceptions during source updates of the binding source property. You can even provide custom logic to specify how the binding engine handles these exceptions by using a UpdateSourceExceptionFilterCallback.

These series of articles will focus on creating and applying custom validation rules the most effective way. I’ll discuss this in an incremental way, building up a structured thinking till an acceptable solution is reached.

Moving on… We should then start with a simple WPF application, with it’s main window as our demo canvas. Lets a text control to fetch some user input to validate, say, a TextBox control, and make it data bound to a property that lives on the Window control itself. We’ll set it as it’s own DataContext for that.

<Window x:Class="ValidationSample1.ValidationWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Rules="clr-namespace:ValidationSample1"
    Title="ValidationWindow" Height="200" Width="300">
    <Window.Resources>

    </Window.Resources>
    <Grid VerticalAlignment="Center" HorizontalAlignment="Center">
        <StackPanel Name="ValidationPanel" >
            <Label>Validated Text Box:</Label>
            <TextBox x:Name="ValidatedTextBox"
                     Width="200"/>
            </TextBox>
        </StackPanel>
    </Grid>
</Window>
#region #using Directives

using System.ComponentModel;
using System.Windows;

#endregion

namespace ValidationSample1
{
    /// <summary>
    /// Interaction logic for ValidationWindow.xaml
    /// </summary>
    public partial class ValidationWindow : Window, INotifyPropertyChanged
    {
        public string myProperty;

        public string MyProperty
        {
            get { return myProperty; }
            set
            {
                if (value == myProperty)
                    return;

                myProperty = value;
                OnNotifyPropertyChanged("myProperty");
            }
        }

        public ValidationWindow()
        {
            InitializeComponent();

            DataContext = this;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}

 Now we need to make sure the user doesn’t submit empty data and visually indicate him that same requirement. For that, we’re going to create a custom ValidationRule to prevent the user from submitting empty text. Like I said, this is done by extending the System.Windows.Controls.ValidationRule class:

#region #using Directives

using System.Globalization;
using System.Windows.Controls;

#endregion

namespace ValidationSample1
{
    public class MandatoryInputRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            if (value != null)
            {
                string input = value as string;

                if (input.Length > 0)
                    return new ValidationResult(true, null);
            }

            return new ValidationResult(false, "Validation error. Field input required.");
        }
    }
}

The next step is to connect the Validation Rule to the binding target. To do so, you can go either the XAML way or in code. I’ll show you the first approach.

<TextBox x:Name="ValidatedTextBox"
         Width="200">
    <TextBox.Text>
        <!--  Textbox notifies changes when Text is changed, and not focus. -->
        <Binding Path="MyProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <!--  Validation rule set to run when binding target is updated. -->
                <Rules:MandatoryInputRule ValidatesOnTargetUpdated="True" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Notice that our binding object sets the UpdateSourceTrigger property to PropertyChanged. This is needed because TextBox updates it’s source when it loses Focus and as such, the default value of UpdateSourceTrigger for the TextBox is LostFocus. In this example, this isn’t enough. We need to analyze the data as user writes. So the ideal update behavior is to trigger when data, or property changes, hence the UpdateSourceTrigger.PropertyChanged value in the preceding example.

For now, our binding is empty value-aware due to the validation rule we applied. But wait! If we run the app, nothing happens. It still doesn’t manifest visually when user submits no text. We must specify a style behavior for when validations errors occur, the user gets warned through visual changes in the control. So we’ll add a style to the Window Resources collection and set it on the TextBox. The XAML would go like this:

<Window x:Class="ValidationSample1.ValidationWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Rules="clr-namespace:ValidationSample1"
    Title="ValidationWindow" Height="200" Width="300">
    <Window.Resources>
        <Style x:Key="EditableTextBox" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
            <Setter Property="Background" Value="#DDFFDD" />
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <Border BorderBrush="Red" BorderThickness="2">
                            <AdornedElementPlaceholder />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="Background" Value="#FFDDDD"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid VerticalAlignment="Center" HorizontalAlignment="Center">
        <StackPanel Name="ValidationPanel" >
            <Label>Validated Text Box:</Label>
            <TextBox x:Name="ValidatedTextBox"
                     Width="200"
                     Style="{DynamicResource EditableTextBox}">
                <TextBox.Text>
                    <!--  Textbox notifies changes when Text is changed, and not focus. -->
                    <Binding Path="MyProperty" UpdateSourceTrigger="PropertyChanged">
                        <Binding.ValidationRules>
                            <!--  Validation rule set to run when binding target is updated. -->
                            <Rules:MandatoryInputRule ValidatesOnTargetUpdated="True" />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>
            </TextBox>
        </StackPanel>
    </Grid>
</Window>

Now, the user sees a red border when no text is submitted. So the task is complete, but I can’t stop wondering the amount of code needed to add validation to a single control. Imagine an entire form…

The next article will present a new approach to this implementation and will require much less code.

* “Goku approach” is an analogy that means one’s accomplished the basic and most objective functional requirement, but then he can’t stop feeling an urge to improve the implementation whether performance, architectural or usability-wise. So that’s when code tends to get upgraded to the next level, shall we say a super level or “super-sayin”, like in the Dragon Ball anime. And after that, the same urge may arise again, and again, giving way to the typical Goku-powerup pattern that the main character faces along the series when needs to evolve to face his enemies :)

Technorati Tags: , ,

Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)

5 comments so far

  1. 001 kacsa

    Hi!

    In WinForms validation if the user input is not valid, user can not leave the control object. How can I make a validation rule which abort to leave the control object.

    Thanks,

    kacsa

    March 21st, 2011
  2. Thanks a very good post

    August 23rd, 2011
  3. 003 sergiesam

    Hi Kacsa, did you already have an answer to your question “Abort to Leave the Control” – I also need this for my ComboBox.
    Thanks,
    Sam.

    October 25th, 2011

Trackbacks

Add a comment