This is the 10th and final post in the series 10 reasons to consider WPF for your next desktop application by Josh Twist, this part explores Validation in WPF.

You can view the other reasons in the series below:

Earlier I introduced a series of posts entitled 10 reasons to consider WPF for your next desktop application. If you haven't read the intro yet, better head on back and read it first.

There are so many great things about WPF that it was hard to choose just 10 for this series. Despite selecting my 10 at the start of the series, there was a little bit of chopping and changing as things progressed. Needless to say, the 10th and final place was a close battle between many great features like fixed-documents, flow-documents and media integration (to name but a few). However, in the end I had to give it to Validation.

One of the reasons I've chosen Validation is that it really demonstrates just how well all the features we've looked at so far (Rich Content, Databinding, Styles etc) all hang together. So let's crack on.

You have probably already noticed that databinding is very forgiving in WPF. Imagine we have a TextBox databound to a DateTime property. What happens when the user types in some nonsense? Not a lot is the answer - if you look closely, you might spot some debug 'goo' in the Output window of Visual Studio:

System.Windows.Data Error: 7 : ConvertBack cannot convert value 'Some nonsense' (type 'String'). BindingExpression:Path=Date; DataItem='MyObject' (Name=''); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') FormatException:'System.FormatException: String was not recognized as a valid DateTime.

So, the databinding engine is aware of the failure but it's just choosing to ignore it and leaves the property with its current value. Not good if the user then chooses to persist the changes (or lack thereof) to the database!

Getting validation up and running is easy and just requires a change to the way we specify the binding:

<TextBox>
    <TextBox.Text>
        <Binding Path="SomeDateProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

The key difference here is the addition of the type ExceptionValidationRule to the binding's ValidationRules. ExceptionValidationRule inherits from ValidationRule and is provided out-of-the-box with WPF and notes exceptions whenever a databound property is updated.

Now look what happens when the user enters some invalid data:

Standard Error Template

As you can see, the TextBox is 'adorned' by a red border.

Now that we've specified some ValidationRules, if one of our rules isn't satisfied the validation runtime will set the Validation.HasError attached property of the control to True. This invokes the display of the control's ErrorTemplate, specified in the attached property Validation.ErrorTemplate. By default this template specifies the control should be wrapped in a nice red border. Cool.

We can even specify our own error template:

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <AdornedElementPlaceholder/>
        <TextBlock Foreground="Red" FontWeight="Bold" FontSize="20">!</TextBlock>
    </DockPanel>
</ControlTemplate>

And attach it to the textbox...

<TextBox Validation.ErrorTemplate="{StaticResource validationTemplate}">
    <TextBox.Text>
        <Binding Path="SomeDateProperty" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Which looks like this:

Custom Error Template

We can do more than just adorn the control in question too. We can make use of styles and triggers to change the control itself.

<Style x:Key="highlightValidationError" >
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="Control.Background" Value="Pink" />
        </Trigger>
    </Style.Triggers>
</Style>

Now, when our textbox input is invalid we can't miss the error!

Custom Error Template

Let's go one step further and have the tooltip show the details of the actual error. We can achieve this using a relative databinding to get the content of the validation error.

<Style x:Key="highlightValidationError" >
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="Control.Background" Value="Pink" />
            <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />
        </Trigger>
    </Style.Triggers>
</Style>

Custom Error Template with tooltip

Custom Validation Rules

You can even write your own validation rules by simply inheriting from ValidationRule. Here's an example StringLengthValidationRule that enforces strings of length between MinLength and MaxLength.

public class StringLengthValidationRule : ValidationRule
{
    public int MaxLength { get; set; }
    public int MinLength { get; set; }

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        string input = value as string;
        int length = (input == null) ? 0 : input.Length;
    
        if (length > MaxLength && MaxLength > 0)
        {
            return new ValidationResult(false, string.Format(
                "Value should be no longer than {0} characters", MaxLength));
        }
        if (length < MinLength)
        {
            return new ValidationResult(false, string.Format(
                "Value should be no shorter than {0} characters", MinLength));
        }
        else
        {
            return new ValidationResult(true, null);
        }
    }
}

Which is used like so:

<Binding.ValidationRules>
    <src:StringLengthValidationRule MaxLength="5" />
</Binding.ValidationRules>

Why not check out the Reason 10 ClickOnce sample to see all this in action. As you'll see, I've gone to town and even used animation to create a flashing border and Xaml to create a glossy exclamation. Yes, I know the flashing border is a dreadful idea but it does show what's possible.

Reason 10 ClickOnce sample

Stay tuned for the next post which will conclude the series and publish the source code for all 10 examples as promised.

Orignally posted by Josh Twist on 18 December 2007 here.