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:

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:

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!

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 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.

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

Post By
Josh Twist
14:35
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
19:52
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
02:01
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
16:39
Josh,
Thank you for your excellent WPF series. Are you planning to upload the source files?
Posted by
Josh
@
08 Jan 2008
05:36
Now available at:
http://www.thejoyofcode.com/10_Reasons_Source_Code.aspx
Posted by
David
@
23 Jan 2008
14:45
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
00:33
Nitpick, but 'less than 5' != 'no longer than 5' (last screenshot)
Posted by
josh
@
06 Jun 2009
00:36
lol - it was a test, you passed ;)
Posted by
Tim
@
11 Aug 2009
12:52
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
03:39
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
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.