Skip Navigation LinksHome > View Post

Reason 10. Validation

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.

Tags: WPF

 
Josh Post By Josh Twist
2:35 PM
18 Dec 2007

» Next Post: WCF Reliable Messaging Demo
« Previous Post: Reason 9. Printing

Comments are closed for this post.

Posted by Robert @ 18 Dec 2007 7:52 PM
Very nice series! Don't let the fact that you said it was going to be 10 reasons stop you from adding more!

Posted by Josh @ 19 Dec 2007 2:01 AM
Cheers Robert, sadly I have to draw the series to an end somewhere. However, don't worry I'll continue to blog about WPF and even some Silverlight in the near future.

I've got some good stuff lined up and maybe even a video or two in mind.

Posted by Andrew @ 06 Jan 2008 4:39 PM
Josh,
Thank you for your excellent WPF series. Are you planning to upload the source files?

Posted by Josh @ 08 Jan 2008 5:36 AM
Now available at: http://www.thejoyofcode.com/10_Reasons_Source_Code.aspx

Posted by David @ 23 Jan 2008 2:45 PM
I don't really see how this is any different than validation in WinForms. All controls have a Validating and Validated event, and its pretty easy to attach a custom component to a Validating event and update the form's visual elements if the input doesn't pass validation. I can (and in fact have) written such a library in about an hour. Now obviously you are using WPF styles when the validation fails, which wouldn't be available in WinForms. But that doesn't make the validation features of WPF a good choice for your 10 reasons.

Posted by Jason @ 06 Jun 2009 12:33 AM
Nitpick, but 'less than 5' != 'no longer than 5' (last screenshot)

Posted by josh @ 06 Jun 2009 12:36 AM
lol - it was a test, you passed ;)

Posted by Tim @ 11 Aug 2009 12:52 PM
In Validation example for WPF, it would be nice to show how to accomplish the same thing in NON-XAML i.e. straight C#...

Posted by saru @ 27 Nov 2009 3:39 AM
nice artice.but my question how can i assign MaxLength and MinLength from code.I want to update Both values dynamically not from designer then how to approach that.

Thank u,
Saru

Posted by john @ 08 Feb 2010 11:09 AM
I've seen the example of displaying the error in the tooltip in several places including here. I have been able to make it work - except - the tooltip does not open unless the mouse is over it. Is that expected? It looks like the tooltip should be open as soon as the error is hit.

© 2005 - 2014 Josh Twist - All Rights Reserved.