Click here to Skip to main content
15,867,594 members
Articles / Desktop Programming / WPF

WPF Validation in a Simple and Efficient Way

Rate me:
Please Sign up or sign in to vote.
4.65/5 (13 votes)
23 Dec 2009CPOL5 min read 62.7K   2K   32   6
How to validate fields in XAML simple and easily

Introduction

Validation and binding in WPF can be quite cumbersome and involves a lot of code. Imagine you have a web application page that involves say 20 input-fields that each should be validated in a particular way... You don't want that code to be plotted down with too much construction just to satisfy simple validations. If you, like me, are a bit lazy in the sense that you prefer simple, fast and elegant solutions, this article might be of interest to you.

Standard approach is to make an Error-Template with an Adorner Placeholder element for the control that is the subject of our validation. Then some trigger to know when to use this, that are bound to a Validation class. It involves some code both in the .cs-behind file and in the XAML file itself. After some hours of adjustments it worked alright for me, but I was not totally satisfied, since I also wanted a key-press validation with a milder form of "information template" that this construction didn't seem to support. Also for some reason it seemed quite slow to bind the message to the ValidationError list.

(Note that in this article I only show some extracts of the code. If you want to test it you must download the source and build it yourself.)

A Solution

An alternative solution to the above described is to define three different styles of the Textbox:

  1. Normal style
  2. Information style (for key-press validation during input)
  3. Error style (on completion of an invalid input)

Error-styled textbox from the sample application:

validationcode.jpg

XAML resource snippet for the Error Style:

XML
<!--Error-style textbox-->
<Style x:Key="textBoxErrorStyle" TargetType="{x:Type TextBox}">
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="FontSize" Value="16"/>
    <Setter Property="ForceCursor" Value="False"/>
    <Setter Property="Foreground" Value="{StaticResource TextColor}"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="3,0,3,0"/>
    <Setter Property="Margin" Value="2"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <Grid Name="test">
                    <Border Background="{StaticResource TextBackColor}"
                        BorderBrush="#d99" x:Name="Bd" CornerRadius="4"
                        BorderThickness="2">
                        <ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
                    </Border>
                    <Image Name="ErrorImage" Width="24" Height="24" Margin="0,0,4,0"
                        Source="Images/error.png" HorizontalAlignment="Right">
                    </Image>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

In the information and error stylings we put an image in the right part of the textbox and attach a tooltip to that image. The text of this tooltip is set dynamically from code-behind when we make an input in textbox or on lost focus. To know what validation we should use for a particular textbox we make use of the Tag-property to set a validation-id. For example, a textbox that should match dates, we might use Tag="Date".

XML
<TextBox
    Style="{StaticResource textBoxNormalStyle}"
    Name="textBox1"
    Tag="Date"
    Grid.Row="1"
    Grid.Column="1" />

Now it's pretty straightforward to create a Hashtable with all regex involved that uses these Tags as Keys. Actually there will be one table for the key-press validation regex and another for the completion regex and another for the associated error messages. If you want you can add a fourth for information messages (if you want other than the error-messages) during key-press.

Alternatively, if you don't like the idea of filling hash tables in code, you might define the strings in an external xml file or perhaps put all strings in the “Resources.resx” file and retrieve it from application / resources. Then it will be easy to alter validation regex without having to recompile the project.

C#
Hashtable previewRegex = new Hashtable();
Hashtable completionRegex = new Hashtable();
Hashtable errorMessage = new Hashtable();

And some useful regex and messages to put into these tables:

C#
previewRegex["Date"] = @"^(\d| |-)*$";
completionRegex["Date"] = @"^(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$";
errorMessage["Date"] = "Write date on the form YYYY-MM-DD";
previewRegex["Email"] = @""; // Needs some keypress regex for valid chars in an email..
completionRegex["Email"] = @"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@(
    [0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$";
errorMessage["Email"] = "Enter a valid email, eg. sven.svensson@sven.se";
previewRegex["Telephone"] = @"^(\d| |-)*$";
completionRegex["Telephone"] = @"^(\d| |-)*$"; // This regex needs some improvement!
errorMessage["Telephone"] = "Valid phonenumbers contains digits, numbers or spaces";

We also need some kind of table to kee track of the faulty validations. In the first version of this article I used a Boolean Hashtable to keep track of any erronious input, however I realized a need to differ from errors and keypress-faults, so we need an extended type with three modes: OK, ERROR and WARNING. This hash can be enumerated and checked against when wanting to submit something relating to this page. I would suggest to bind a dependency property of type Boolean that is connected to the enabled state of some submit-button for the form in question. If this table contains only OK states it means the whole form / page is filled in correctly.

C#
enum ValidationStates { OK, ERROR, WARNING };

Hashtable validationState = new Hashtable();

Finally to make all these things to work together we should attach some events to the textboxes in our XAML layout definition.

The events you should consider are:

  1. PreviewTextInput event for the key-press validation on input.
  2. LostFocus event for the completion validation process.
  3. GotFocus event for the initiation of validation.
XML
<TextBox
    Style="{StaticResource textBoxNormalStyle}"
    Name="textBox1"
    PreviewTextInput="KeypressValidation"
    LostFocus="CompletionValidation"
    GotFocus="InitValidation"
    Tag="Date"
    Grid.Row="1"
    Grid.Column="1" />

In this solution I use regex for all my validations. Believe me, you can go very far with just regex, and it's a very clean and neat way to validate. Read more about it on http://www.codeproject.com/KB/recipes/RegexTester.aspx. It's an excellent article.

Now, since we already have the id of the validation regex to use from the textboxes “Tag property” we only need three methods attached to these three events for all the textboxes, no matter how many. (This is what makes this solution simpler than standard approach).

Snippet for the method that is attached to the KeypressValidation event (note: we use the same method for all textboxes!). It is similary to the CompleteValidation method.

C#
private void KeypressValidation(object sender, TextCompositionEventArgs e)
{
    // Handle to the textbox tjhat should be validated..
    TextBox tbox = (TextBox) sender;
    // Fetch regex..
    Regex regex = new Regex((string)previewRegex[(string)tbox.Tag]);
    // Check match and put error styles and messages..
    if(regex.IsMatch(e.Text))
    {
        if((ValidationStates)validationState[tbox.Name] !=
          ValidationStates.OK) tbox.Style = (Style) FindResource("textBoxNormalStyle");
        validationState[tbox.Name] = ValidationStates.OK;
    }
    else
    {
        if((ValidationStates)validationState[tbox.Name] != ValidationStates.WARNING)
        {
            tbox.Style = (Style) FindResource("textBoxInfoStyle");
            validationState[tbox.Name] = ValidationStates.WARNING;
            // Very important if want to use Template.FindName when changing
            // style dynamically!
            tbox.UpdateLayout();
        }
        // Fetch the errorimage in the tbox:s control template..
        Image errImg = (Image)tbox.Template.FindName("ErrorImage", tbox);
        // And set its tooltip to the errormessage of the textboxs validation code..
        errImg.ToolTip = (string)errorMessage[(string)tbox.Tag];
        // Use this if you dont want the user to enter something in textbox
        // that invalidates it.
        e.Handled = true;
    }
}

I must admit I had to waste some time to figure out why the line Image errImg = (Image) tbox.Template.FindName("ErrorImage", tbox); that is supposed to return the error image element from the textbox:s control template always returned null. After a good thought I realized that since I set the template style that contains the ErrorImage element just before I try to get this element, I must first Update the Layout in order for this style to really be set. tbox.UpdateLayout(); made it work. Another note of interest is the e.Handled = true; on the end of the block that handles the invalid input. Since the event is a PreviewTextInput it means the text is not yet comitted to the textbox:s text-property. By this directive we tell the event manager that the task is already handled and thus no text are written. If you don't like such a restriction just remove that line.

I hope this alternative way to validate a XAML presentation page could improve your projects!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer HRM Software AB
Sweden Sweden
Jonas Wranå, software developer from Sweden, currently working at HRM Software AB, a company that develop a major salary and resource planning system. Some other interests includes mathematics, fractal geometry, pedagogy, music composition and photography.

Comments and Discussions

 
GeneralMy vote of 4 Pin
nim2746-Feb-13 5:36
nim2746-Feb-13 5:36 
GeneralRe: My vote of 4 Pin
angad277-Apr-14 2:50
angad277-Apr-14 2:50 
GeneralMy vote of 3 Pin
govind20076-Feb-11 22:39
govind20076-Feb-11 22:39 
GeneralFantastic! Pin
Rlife4-Mar-10 8:54
Rlife4-Mar-10 8:54 
GeneralRe: Fantastic! Pin
Jonas Wranå8-Mar-10 20:31
Jonas Wranå8-Mar-10 20:31 
GeneralRe: Fantastic! Pin
Rlife7-Apr-10 4:12
Rlife7-Apr-10 4:12 

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.