WPF: Validation made easy with IDataErrorInfo


Recently I came across an interface called IDataErrorInfo used for validation, and apparently it’s been around since the early days. I’ve never seen any use of it, or heard of it’s usage until now. It’s been used with WPF validation and integrates very seamlessly too.

  1. Step 1: Create data model with IDataErrorInfo
  2. Step 2: Databind the data to your input fields
  3. Step 3: Customize the Error Template to get different look and feel

WPFValidation

As you can see from the screenshot, validation is triggered when the fields input fail the criteria. The “Add” button on the screen is also disabled/enabled in accordance to the validation via the use of WPF Command System.

Step 1: Creating data model with IDataErrorInfo

To get started with this, you first need a model that implements IDataErrorInfo, like so…

public class Customer : IDataErrorInfo
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    #region IDataErrorInfo Members

    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    public string this[string columnName]
    {
        get
        {
            string result = null;
            if (columnName == "FirstName")
            {
                if (string.IsNullOrEmpty(FirstName))
                    result = "Please enter a First Name";
            }
            if (columnName == "LastName")
            {
                if (string.IsNullOrEmpty(LastName))
                    result = "Please enter a Last Name";
            }
           if (columnName == "Age")
            {
                if (Age < = 0 || Age >= 99)
                    result = "Please enter a valid age";
            }
            return result;
        }
    }

    #endregion
}

Looking at the interface implementation, we put in checks for the Property Names we are interested in validating, and return an error message for each one that failed the criteria. For example, we are validating that the first and last names are not empty fields.

Step 2: Databind the data to your input fields

So how does WPF know how to use this interface to validate the input fields? We make use of the coolness of data binding! Below is a snippet of a TextBox with data binding.

<textbox x:Name="tbFirstName" Grid.Row="0" Grid.Column="1" Validation.Error="Validation_Error"
         Text="{Binding UpdateSourceTrigger=LostFocus, Path=FirstName,
                ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />

UpdateSourceTrigger specifies the condition for updating the source. I have set it to LostFocus instead of PropertyChanged, because PropertyChanged will degrade performance by triggering an update on every keystroke. There also another option called Explicit, meaning you have to trigger the update manually by calling the UpdateSource method.

ValidatesOnDataError is the super star behind all this. By flipping this flag on, the databinding will be able to communicate with any data types that implement IDataErrorInfo. (You might also want to read more about Validation.Error event and NotifyOnValidationError.)

Now that we understand how all these tie together, next question is how to display the error messages. By default, WPF notifies user of a error by highlighting the input field with a red border only. To display the error message or create a whole new look (like how I’ve done it), you need to do some work.

There’s a Validation static class in the WPF framework that’s designed for supporting data validation. You can attach it’s properties and event to any UIElement, such as the Validation.Errors property which holds a collection of error messages. We can now make use of this property to display our error message, for example in a ToolTip when user mouse overs the error icon.

Step 3: Customize the Error Template to get different look and feel

In WPF you can customize almost anything on the UI which is awesome. In order to customize our look and feel of the error, we change the ErrorTemplate of our textbox by defining a new style.

<style TargetType="{x:Type TextBox}">
    <setter Property="VerticalAlignment" Value="Center" />
    <setter Property="Margin" Value="0,2,40,2" />
    <setter Property="Validation.ErrorTemplate">
        </setter><setter .Value>
            <controltemplate>
                <dockpanel LastChildFill="true">
                    <border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
                            ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                        <textblock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white">
                        </textblock>
                    </border>
                    <adornedelementplaceholder Name="customAdorner" VerticalAlignment="Center" >
                        <border BorderBrush="red" BorderThickness="1" />
                    </adornedelementplaceholder>
                </dockpanel>
            </controltemplate>
        </setter>
</style>

I’ve drawn the error icon very simply by wrapping a Border (with corner radius set properly) around a TextBlock. What’s of interest here is the AdornedElementPlaceholder, which like the name implies is a placeholder for an Adorner. So what is an Adorner? From my limited understanding, it’s basically a FrameworkElement that sits on top of your UI control on a different layer that is responsible for displaying visual cues. To me, it resembles a lot like the Decorator pattern, where you can enhance the look of your bound control without affecting it’s default state and behavior.

An example of customizing the Adorner is in the snippet above, where I put a red Border in it. When a validation error occurs, the Border appears around the control it is bound to, in this case the TextBox.

Lastly this line of Xaml code binds the ToolTip content to the first error message in the Validation.Errors collection.

ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"

WPF has loads of features, and I’m enjoying the process of discovery. It’s been great fun playing with it, and there will be more to come. Happy Coding.

Updated Source
Download the sample code here.

Share this post:
About these ads
Posted in WPF. 23 Comments »

23 Responses to “WPF: Validation made easy with IDataErrorInfo”

  1. WPF: Validation Summary Control « Code Blitz Says:

    [...] WPF: Validation made easy with IDataErrorInfo [...]

  2. Veena Says:

    Nice article!!

  3. Scott Says:

    Very nice article!
    I have a couple of questions though

    Why do your text boxes not show the error when the program is loaded?

    I cannot seem to hide the error template after it has been shown, is it possible to hide the error template?
    (setting visibility to “Hidden” on the dock panel, textbox, or placeholder does not seem to do it)

    Thanks for helping me, I am new to WPF. Your validation example has saved me a ton of time.

    • Ed Foh Says:

      Hi Scott

      thanks for the question. I’m also still learning WPF, there’s just too much! :)

      I had a look at my code again, found a bug and fixed it too..lol. Anyways the reason why it’s not showing the error when program starts is due to the cause of binding the grid’s DataContext to a instance of a Customer as a resource key. I modified code to bind the grid to an initialized instance of a Customer in the code behind, and error triggered on startup. I’m not a 100% sure why that is, but that was my first suspicion.

      for your 2nd question, because the error is triggered on and off via databinding and the IDataErrorInfo interface, to turn it off requires a valid value in the textbox. It’s triggered on LostFocus, so click somewhere else on the screen and validation will occur.

      Hope that helps. You can download the updated sample code. I have modified it to trigger error on startup.

      • Scott Says:

        Thank you for such a detailed answer and you are super fast too. I was thinking/hoping I would get the reply sometime next week as this post has been around awhile.

        I am looking forward more to net 4.0 as there will be better support for validation. (well that and more)

        You have helped me a ton. Thanks.

        Have Fun,
        -Scott

  4. Hugo Says:

    Hi. Great article. I’m having a bit of a problem showing the styles after validation, but I’m working on that. I have a question though: How would you implement such validation on a PasswordBox control, given it doesn’t support binding?

    • Ed Foh Says:

      Hi Hugo

      thanks. First of all, this approach only works with databinding. I guess I’m wondering why you would want to databind to a PasswordBox Control. I’m thinking the WPF Team left this out because the password string would have to be stored in memory for databinding to work, thus becoming a security concern.

      I guess without databinding, you can still validate it yourself. If you still want to make use of IDataErrorInfo, there are other approaches, such as creating a custom control that inherits from PasswordBox and creating a dependency property for databinding. Or using Attached Properties which is a quicker approach.

      Here’s a good article that uses Attached Properties.

      http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html

      hope this helps.
      Ed

      • Sirius Says:

        <>

        For example to let a user know they’ve not entered a password or that the password and comparison password you’ve asked them to enter match.

        Microsoft’s approach may work for them. However there are many scenarios when its doesn’t. If someone has gone to the lengths of hacking your code to grab a password, that it’s not dependency property and cannot be bound is not going to stop them.

        The functionalfun approach doesn’t play nice with MVVM frameworks like Caliburn.

  5. Dasha Says:

    This is awesome! Helped me a great deal but I can’t seem to figure out how to validate other controls such as ComboBox and Date-pickers. Any suggestions?

  6. WPF: Validation made easy with IDataErrorInfo « haipk Says:

    [...] Step 1: Create data model with IDataErrorInfo [...]

  7. Paras Sharma Says:

    Is there any way by which initially the red lines are not shown and when the user clicks on Add button validation takes place and if any error is there we can show the Red line?

  8. Nadege Says:

    Great thanks for this post. It’s very clear.

  9. Edgar Says:

    This is great!!!
    I have only one question, How can I validate the controls only when is enabled?

  10. youssef Says:

    Hi,
    I have used your validation in my program, but d’ont work.
    I use the same style for TextBox

    the difference to your exaple.
    i have a usercontrol and no window

    and the TextBox

    and in code behind:
    string IDataErrorInfo.Error
    {
    get
    {
    throw new NotImplementedException();
    //return null;
    }
    }

    string IDataErrorInfo.this[string propertyName]
    {
    get { return this.GetValidationError(propertyName); }
    }

    string GetValidationError(string propertyName)
    {
    if (Array.IndexOf(ValidatedProperties, propertyName) < 0)
    return null;

    string error = null;

    switch (propertyName)
    {
    case "ArtikelNr":
    error = this.ValidateArtikelNr();
    break;
    case "Pruefanweisung":
    error = this.ValidatePruefanweisung();
    break;
    case "SabMin":
    error = this.ValidateSabMin();
    break;
    case "SabMax":
    error = this.ValidateSabMax();
    break;
    case "HysMin":
    error = this.ValidateHysMin();
    break;
    case "HysMax":
    error = this.ValidateHysMax();
    break;

    case "TlBezeichnung":
    break;

    default:
    Debug.Fail("Unexpected property being validated on Lasertrimming data: " + propertyName);
    break;
    }

    return error;
    }

    #region ArtikelNr validation
    string ValidateArtikelNr()
    {
    if (IsStringMissing(this.ArtikelNr))
    {
    return "missing article number"; //TODO: Text der fehlermeldung in Ressource
    }
    else if (!IsValidArtikelNr(this.ArtikelNr))
    {
    //return "invalid Artikel number. at least 2 characters and only numbers and – are allowed"; //TODO: Text der fehlermeldung in Ressource
    return "invalid Article number"; //TODO: Text der fehlermeldung in Ressource
    }
    return null;
    }

    static bool IsStringMissing(string value)
    {
    return
    String.IsNullOrEmpty(value) ||
    value.Trim() == String.Empty;
    }

    static bool IsValidArtikelNr(string artikelNr)
    {
    if (IsStringMissing(artikelNr))
    return false;

    string artikelNrPattern = @"^[0-9]+-?[0-9]+$"; // kann nur Zahlen und bindestrich(-) enthalten. mindestens zwei Zeichen
    return Regex.IsMatch(artikelNr, artikelNrPattern, RegexOptions.IgnoreCase);
    }
    #endregion

  11. ram Says:

    error at line “” when i try to use the code copied from this site.

  12. WPF MVVM Validation using Fluent.Validation | 2bit-coder Says:

    [...] found some great posts on using IDataErrorInfo with WPF, but this wasn’t as straight forward when using an MVVM model.  Then I found a great post on [...]

  13. Itsho Says:

    Here’s your Style (formatted), for anyone having trouble copy&paste it :-)

  14. Itsho Says:

    Here’s your Style (formatted), for anyone having trouble copy&paste it :-)

    http://pastebin.com/j8KAGtwA


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 97 other followers

%d bloggers like this: