Click here to Skip to main content
15,866,422 members
Articles / Desktop Programming / WPF
Article

Validation in Windows Presentation Foundation

Rate me:
Please Sign up or sign in to vote.
4.87/5 (134 votes)
19 Aug 200612 min read 876.7K   18.7K   263   91
In this article, I'll walk you through using the built-in Validation classes that exist in Windows Presentation Foundation. I'll then discuss an alternative approach to validation that might suit a richer domain layer, by creating a custom WPF ErrorProvider.

Sample Image - wpfvalidation3.png

Introduction

Since the dawn of software development, the single biggest threat to reliable information has been end-users. After decades of the existence of computers, why do we still need to inform users that "the Start Date should always be less than the End Date", or that "First Name is a required field"? Since User.Current.Electrocute() won't be added to the .NET Framework until .NET 4.0, most of us generally have to resort to displaying some kind of UI cues to politely tell the user what they've done wrong, and how they can fix it.

Under Windows Forms, we had the ErrorProvider, and under ASP.NET, we had Validator controls. In Windows Presentation Foundation, the approach is going to change, but the goal remains the same: telling users that something is wrong. In this article, I'll discuss how input validation is designed to be handled in Windows Presentation Foundation (hereafter referred to as WPF). Specifically, I'll discuss the ExceptionValidationRule class, creating custom ValidationRules, how to display errors, when to display errors (UpdateSourceTriggers), and finally, an alternative approach using an implementation of the Windows Forms ErrorProvider for WPF.

Table of Contents

  1. Welcome to WPF Validation
  2. Very Simple Validation: ExceptionValidationRule
  3. Displaying Errors
  4. Customized ValidationRules
  5. Controlling When to Validate: UpdateSourceTriggers
  6. Where Does an ErrorProvider Fit in?
  7. IDataErrorInfo
  8. Creating our ErrorProvider
  9. Conclusion
  10. Special Thanks

Welcome to WPF Validation

If you never spent much time with Windows Forms or data binding, I'm hoping that this article will remain simple enough to follow along. Validation in Windows Presentation Foundation takes an approach very similar to ASP.NET validation, in that most "business rules" are enforced on the user interface and applied to specific controls and bindings. This approach is quite simple to understand and implement, but some proponents of "rich domain models" and object-oriented designs (including myself) have some problems with it. As we approach the end of the article, I'll discuss why this is and a way to do things differently, but still leveraging some of the strengths of WPF.

On the other hand, if you did spend a lot of time using Windows Forms, and made heavy use of the ErrorProvider/IDataErrorInfo approach to validation, you might be disheartened to know that there isn't one in Windows Presentation Foundation. Luckily, since I too was disappointed about having to enforce everything "on the UI", towards the end of the article, I'll show how to create an ErrorProvider for use in your Windows Presentation Foundation applications. However, even if you're convinced that validating on the UI is a Bad Idea (TM), I'd encourage you to read the full article as I'll discuss some of the other validation features in WPF that you can still leverage.

Warning: All of the sample code was written using the BETA 2 release of .NET 3.0, and subsequently, some of it may have changed. Hopefully, the concepts will stay the same though, otherwise this will be a very wasted article :)

Very Simple Validation: ExceptionValidationRule

Most of the time when we talk about validation, we're talking about validating user input (other kinds of validation are outside the scope of this article). Let's look at the most simple kind of built-in validation offered by WPF - the ExceptionValidationRule. To begin our example, let's create a simple Customer class:

C#
public class Customer 
{
    private string _name;
    
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

Writing classes is fun, but you'll probably never convince people to pay you unless you can add some kind of UI to let them interact with your classes. Since this article is about Windows Presentation Foundation, let's use XAML to create our UI. As we're smart developers, we'll also use data binding:

XML
<TextBox Text="{Binding Path=Name}" />

Before we go any further, it's important to note that the odd-looking markup above is actually just a kind of shorthand for writing this:

XML
<TextBox>
    <TextBox.Text>
        <Binding Path="Name" />
     </TextBox.Text>
</TextBox>

Now, let's say one of your requirements is that customer names are mandatory. To implement this constraint, you could change your customer's Name property to look like this:

C#
public string Name
{
    get { return _name; }
    set
    {
        _name = value;
        if (String.IsNullOrEmpty(value))
        {
            throw new ApplicationException("Customer name is mandatory.");
        }
    }
}

Using WPF Validation Rules on our data binding, we can display this error automatically. All we need to do is make use of the ValidationRules property on our binding, like this:

XML
<TextBox>
    <TextBox.Text>
        <Binding Path="Name">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

If you ran this code, you'd see something which looks like this:

A WPF window showing a TextBox that has been given a red border, indicating that it is invalid.

Displaying Errors

Windows Presentation Foundation has a static class inside the System.Windows.Controls namespace, imaginatively named "Validation". It has a number of static dependency properties which you can apply to any control. The ones we're most interested in are:

  • Errors - A list of error messages that apply to the bindings on this control.
  • HasError - Indicates whether there are any errors in the Errors property.
  • ErrorTemplate - A ControlTemplate we can apply if there is an error.

By default, the Validation class uses an ErrorTemplate that has a red border around the control, which is why we get a red border in the program above. If the default red borders aren't your thing, I'll understand. You might want thick green borders with orange exclamation marks:

A custom control template used to display errors, with a green border and orange exclamation marks.

To achieve the look you're after, you could define your own control template:

XML
<Application.Resources>
  <ControlTemplate x:Key="TextBoxErrorTemplate">
    <DockPanel LastChildFill="True">
      <TextBlock DockPanel.Dock="Right" 
        Foreground="Orange" 
        FontSize="12pt">!!!!</TextBlock>

      <Border BorderBrush="Green" BorderThickness="1">
         <AdornedElementPlaceholder />
      </Border>
    </DockPanel>
  </ControlTemplate>
</Application.Resources>

The AdornerElementPlaceholder is used to say "put the invalid control here". Once you've created this template, you can then reuse it on your customer name TextBox by setting the Validation.ErrorTemplate attached property:

XML
<TextBox 
    Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}"> 
    [...]
    <TextBox>

Or, to save having to set the ErrorTemplate every time, you could do it in a WPF Style. If we were to set the style's TargetType to TextBox, and don't use a key, all text boxes in our application will automatically receive this style:

XML
<Style TargetType="{x:Type TextBox}">
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <DockPanel LastChildFill="True">
                    <TextBlock DockPanel.Dock="Right" 
                        Foreground="Orange"
                        FontSize="12pt">
                        !!!!
                    </TextBlock>
                    <Border BorderBrush="Green" BorderThickness="1">
                        <AdornedElementPlaceholder />
                    </Border>
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now, green borders are all well and good, but they don't exactly tell the users what they've done wrong. Since we included the error message in the exception we were throwing earlier, we can make use of the static Validation.Errors attached property to get this value and use it as a property on our TextBox. The most common example is to set the ToolTip, like so:

XML
<Style TargetType="{x:Type TextBox}">

    [... SNIP: The code from above ...]
    
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip"
                Value="{Binding RelativeSource={RelativeSource Self}, 
                       Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>

Now, if we mouse over the TextBox while it's in an error state, we'll get a tool-tip telling us what we did wrong:

An ErrorTemplate with the ToolTip for the TextBox set to the error message.

All we need to do is place that style in our application resources, ensure we apply any validation rules to our bindings, and voila!

On a more advanced note, if you would rather show the errors somewhere else than in the ToolTip, such as in the TextBlock where the orange exclamation marks were, you could use a style like this:

XML
<Style TargetType="{x:Type TextBox}">
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <DockPanel LastChildFill="True">
                    <TextBlock DockPanel.Dock="Right"
                        Foreground="Orange"
                        FontSize="12pt"
                        Text="{Binding ElementName=MyAdorner, 
                               Path=AdornedElement.(Validation.Errors)
                               [0].ErrorContent}">
                    </TextBlock>
                    <Border BorderBrush="Green" BorderThickness="1">
                        <AdornedElementPlaceholder Name="MyAdorner" />
                    </Border>
                </DockPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

An ErrorTemplate with a green border and the error message being displayed in a TextBlock as part of the adorner.

Customized Validation Rules

I know what you're thinking: "Do I have to throw exceptions?"

I'm glad you asked. If you remember our customer's Name property above, you'll remember we were throwing an exception in order to display the errors using an ExceptionValidationRule. I'm sure most people will agree that throwing exceptions isn't really the optimal way of signaling that we have user input errors. Apart from the performance penalties, personally, I believe that "user input" errors aren't exactly "exceptional", and so they are not exactly what exceptions were designed for.

If you hunt around the framework, you'll see that the ExceptionValidationRule class inherits from an abstract ValidationRule class. The ValidationRule class looks like this:

C#
public abstract class ValidationRule
{
    public abstract ValidationResult Validate(
        object value, 
        CultureInfo culture);
}

When we created our binding earlier, I had some XAML that resembled this:

XML
<Binding Path="Name">
    <Binding.ValidationRules>
        <ExceptionValidationRule />
    </Binding.ValidationRules>
</Binding>

The truth is, we could have used anything that inherits from the ValidationRule base class. Let's create a custom ValidationRule designed just to validate the lengths of strings.

C#
namespace MyValidators
{
    public class StringRangeValidationRule : ValidationRule
    {
        private int _minimumLength = -1;
        private int _maximumLength = -1;
        private string _errorMessage;
        
        public int MinimumLength
        {
            get { return _minimumLength; }
            set { _minimumLength = value; }
        }
        
        public int MaximumLength
        {
            get { return _maximumLength; }
            set { _maximumLength = value; }
        }
        
        public string ErrorMessage
        {
            get { return _errorMessage; }
            set { _errorMessage = value; }
        }
        
        public override ValidationResult Validate(object value, 
            CultureInfo cultureInfo)
        {
            ValidationResult result = new ValidationResult(true, null);
            string inputString = (value ?? string.Empty).ToString();
            if (inputString.Length < this.MinimumLength ||
                   (this.MaximumLength > 0 &&
                    inputString.Length > this.MaximumLength))
            {
                result = new ValidationResult(false, this.ErrorMessage);
            }
            return result;
        }
    }
}

Our validator has three properties: a MinimumLength, a MaximumLength, and an ErrorMessage string which is shown if the value doesn't fit within the range. We're now ready to use it in our XAML.

First, we'll need to reference it in our XML namespaces at the top of the file:

XML
<Window [...]
    xmlns:validators="clr-namespace:MyValidators" />

Now we can add it to our binding's ValidationRules:

XML
<Binding Path="Name">
    <Binding.ValidationRules>
         <validators:StringRangeValidationRule 
            MinimumLength="1" 
            ErrorMessage="A name is required." />
     </Binding.ValidationRules>
</Binding>

If you've added the styles I gave above, that should be all you need to do to show validation messages.

Controlling When to Validate: UpdateSourceTriggers

If you ran any of the sample code above, you'll notice that the validation only takes place when you hit Tab or click away from the text box. That's a good default for most cases, but if you'd like validation to take place at other times, you can use the UpdateSourceTrigger property on your binding.

The UpdateSourceTrigger property can take one of three possible values:

  • The default is LostFocus, which means that the bound data source will be updated and validated when the UI element loses focus (such as when you tab away, or click on another element).
  • The second option is PropertyChanged, which occurs when the property you are binding to (the TextBox's Text property in the example above) is changed. This enables you to validate whenever the user types or changes the value, without having to wait until they tab away (this is great for filtering a list of things).
  • Finally, there's the Explicit option, which only happens when you tell it to happen.

To make use of the UpdateSourceTrigger property in our code, all we'd need to do is set it in our binding:

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

For more information on UpdateSourceTriggers, see the MSDN page for the UpdateSourceTrigger enumeration.

Where Does an ErrorProvider Fit in?

We've already discussed that the ExceptionValidationRule isn't a nice way of validating. Some of the problems you may have with creating custom validation rules as I showed above are:

  1. Our business "rules" are defined in markup, hidden inside our bindings, and can be hard to locate and maintain, as well as difficult to reuse.
  2. We can't easily get a "list" of all of the broken rules on a form in order to perform other logic - such as determining if the customer can be saved when the "Save" button is clicked.

Defining business rules in markup may not be such a problem for some applications, but in applications with rich business objects such as Trial Balance, or applications using rich frameworks such as CSLA, or in cases where the rules are shared across many screens or applications, you may find this to be a big limitation. If this isn't a problem for you, you might not get much out of the rest of this article.

This means that the ValidationRules approach isn't very useful to some of us. However, all is not lost, as we can still make use of the static Validation class, and of its use of styles and control templates to control the display of errors.

IDataErrorInfo

The IDataErrorInfo interface has been kicking around since .NET 1.1, and was used heavily in DataSets as well as used by many business object frameworks. You'll find this gem in the System.ComponentModel namespace. If you've been reading my blog for some time, hopefully this interface isn't a stranger to you.

IDataErrorInfo was designed for reporting errors to user interface controls from the objects they are bound to. The DataGrid (1.1) and DataGridView (2.0) in Windows Forms both detected the presence of this interface on objects they were bound to automatically, and showed any errors without any work. The Windows Forms ErrorProvider could automatically be used to display errors on any control that came from the objects they (and the ErrorProvider) were bound to, all without any extra code being written.

To refresh your memory, this is how we would have used this interface in .NET 2.0:

C#
public class Customer : IDataErrorInfo
{
    private string _name;
    
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
    
    public string Error
    {
        get
        {
            return this[string.Empty];
        }
    }
    
    public string this[string propertyName]
    {
        get
        {
            string result = string.Empty;
            propertyName = propertyName ?? string.Empty;
            if (propertyName == string.Empty || propertyName == "Name")
            {
                if (string.IsNullOrEmpty(this.Name))
                {
                    result = "Name cannot be blank!";
                }
            }
            return result;
        }
    }
}

Back then, all we had to do was bind a grid to our Customer object, and any errors would be automatically reported. If we bound the Customer's Name to a TextBox, we just had to drag an ErrorProvider onto the form, set its DataSource to our Customer object, and all of our validation was done for us.

Sadly, IDataErrorInfo appears to have been benched in WPF, as there doesn't seem to be any built-in support for it. This means that there is no ErrorProvider component in WPF either. Since the concept of an ErrorProvider is pretty simple, let's have a go at making one ourselves.

More on IDataErrorInfo...

The MSDN entry for IDataErrorInfo is a good starting point. For a deeper discussion of the IDataErrorInfo interface and a better way to implement business object validation, you might enjoy my recent Delegates and Business Objects article on the CodeProject. Rocky Lhotkas' brilliant CSLA architecture also makes extensive use of the IDataErrorInfo interface.

Creating our ErrorProvider

The ErrorProvider that I have built (found in the attached sample code) inherits from the WPF Decorator class, which means you can "put stuff inside it". To use it, you just need to do something like this:

XML
<validators:ErrorProvider>
    <StackPanel>
        <TextBox Text="{Binding Path=Name}" />
        <TextBox Text="{Binding Path=Age}" />
    </StackPanel>
</validators:ErrorProvider>

My ErrorProvider works by cycling through all of the controls that are inside of it, and looking for any data bindings on their properties. When a value of one of the bound properties changes, it checks if its DataContext implements IDataErrorInfo, and if so, it gets any error messages, and displays them using the built-in static Validation class. That means you can use the styles and control templates that I showed above, whilst keeping all of your validation logic in another class.

Cycling through the control hierarchy in WPF is done using the LogicalTreeHelper class. The code itself is a little long to post here, but is included in the sample code download at the top of the page.

Using the WPF ErrorProvider brings a number of benefits:

  • Your XAML is much more compact, as you don't need to add a list of ValidationRules to each binding.
  • All of your validation can be done by your business objects, rather than at the UI. This lets you have great encapsulation on your objects while still having an informative UI.
  • You can call the Validate() method on the ErrorProvider to force validation, and check if the controls are valid, rather than having to inspect each one.
  • You can use the GetFirstInvalidElement() method to get the first control with an error on your form, to set focus to it easily. This is great if, for example, your "Save" button has been clicked but you want to show them that they still have errors.

Conclusion

Whether you choose to use custom ValidationRules, my ErrorProvider, or simply throw exceptions, I hope I have given you enough information to make an educated decision that suits your project.

If your applications have a very rich domain model, you may find the ErrorProvider approach helpful. If your application is very service oriented, or very UI centric, ValidationRules might be all you need.

Of course, if none of these approaches suit your requirements, you could always hold out for .NET 4.0 and the introduction of User.Current.Electrocute() :)

Special Thanks

I would like to give a special thanks to Paul Czywcynski of TempWorks who has done a lot of testing with my ErrorProvider and given me a couple of bug fixes to make it work in a few places that I hadn't tried.

I'd also like to thank Mike Brown, who pointed me to the LogicalTreeHelper and also pointed me to ValidationRules (of course, he didn't mention them until after I'd done my ErrorProvider :) ).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Octopus Deploy
Australia Australia
My name is Paul Stovell. I live in Brisbane and develop an automated release management product, Octopus Deploy. Prior to working on Octopus I worked for an investment bank in London, and for Readify. I also work on a few open source projects. I am a Microsoft MVP for Client Application Development.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Eric Ouellet2-Nov-11 14:31
professionalEric Ouellet2-Nov-11 14:31 
2011-11 and still very helpfull. Thanks a lot !

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.