Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Validation - using INotifyDataErrorInfo

0.00/5 (No votes)
15 Feb 2015 1  
Focus on WPF Novice, to do Data Validation using INotifyDataErrorInfo interface easily

Introduction

Part I: implements IDataErrorInfo interface

I really want to say thanks to readers who read my previous post “WPF Validation – using DataErrorInfo” for their comments and wishes.

Now I got time to write another post in Part - II “WPF Validation – using INotifyDataErrorInfo”.

Using the Code

It is always better to have some theoretical knowledge before implementing the same.

Let’s dive into that.

Implementation of INotifyDataErrorInfo interface returns 3 things:

  • HasErrors bool property
  • GetErrors IEnumerable type
  • ErrorsChanged – Event

Two things are the main difference compared to IDataErrorInfo interface implementation.

  • Checks the validation asynchronously
  • The ability to store and retrieve multiple errors for single property

We will see this in a very detailed way in the below part of this post.

First, we can see the coding side, then go for XAML styling later.

Step 1

Implement INotifyDataErrorInfo interface in Customer class.

public class Customer : INotifyDataErrorInfo
{
       public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;     

       public System.Collections.IEnumerable GetErrors(string propertyName)
       { }
       public bool HasErrors
       {
          get
          {              
            return true;
           }
       }
}

Step 2

Define Dictionary<string, List<string>> locally.

Dictionary<string, List<string>> propErrors = new Dictionary<string, List<string>>();

Here is one for property name, another one for list of errors for the given property.

Step 3

We should check the HasErrors property, whether it returns true or not. If it returns true, then only data validation will be highlighted. Data validation will not be notified to the user when errors content is there but this property returns false. So make sure that this property returns true based on error values count from propErrors.

public bool HasErrors
{
   get
   {
      try
      {
         var propErrorsCount = propErrors.Values.FirstOrDefault(r =>r.Count>0);
         if (propErrorsCount != null)
              return true;
          else
              return false;
       }
       catch { }
       return true;
    }
 }

Step 4

We should get the list of defined errors from the Dictionary.

public System.Collections.IEnumerable GetErrors(string propertyName)
{
     List<string> errors = new List<string>();
     if (propertyName != null)
     {
         propErrors.TryGetValue (propertyName, out errors);
         return errors;
     }
     else
         return null;
}

Step 5

In the OnPropertyChanged() method, we call Validate() to get the errors for each property we have in the Customer class.

Some cases, we will have more validation strings for one property. Those defined validation strings will be added into List<string> listErrors; this list is assigned to dictionary.

propErrors ["Name"] = listErrors;

If listErrors. Count > 0, then raise the ErrorsChanged Event.

That's all coding. Now we can go for styling in XAML.

XAML part - Styling part remains the same as what we have done for IDataErrorInfo implementation. Only one thing is replace ValidatesOnDataErrors with ValidatesOnNotifyDataErrors property to true.

<Style x:Key="TextErrorStyle" TargetType="{x:Type TextBox}">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate x:Name="TextErrorTemplate">
                        <DockPanel LastChildFill="True">
                            <AdornedElementPlaceholder>
                                <Border BorderBrush="Red" 
                                BorderThickness="2"/>                       
                            </AdornedElementPlaceholder>
                            <TextBlock FontSize="20" 
                            Foreground="Red">*?*</TextBlock>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" 
                Value="True">                    
                    <Setter Property="ToolTip" 
                    Value="{Binding RelativeSource=
            {x:Static RelativeSource.Self},
            Path=(Validation.Errors)[0].ErrorContent}"></Setter>
                </Trigger>
            </Style.Triggers>
</Style> 

Then bind this style to the text box control which we want to validate.

<TextBox HorizontalAlignment="Left" Margin="10" 
x:Name="SampleTextBox" Height="35" Width="150" 
Style="{StaticResource TextErrorStyle}"                 
   Text="{Binding Source={StaticResource CustomerInstance},Path=Name,Mode=TwoWay,
   ValidatesOnNotifyDataErrors=True,UpdateSourceTrigger=PropertyChanged}">            
</TextBox> 

Here Source is nothing but the Customer class instance:

xmlns:local="clr-namespace:WpfValidation"
<Window.Resources>
     <local:Customer x:Key="CustomerInstance" Name="Welcome"/>
</Window.Resources>

That’s all guys. :)

I've attached the code sample for your easy reference. Anyway, if you want to give it a try, then just get my previous post code sample, from there you can just try to implement this concept.

Hope you all got this concept well. I welcome your comments, questions and suggestions if any.

Points of Interest

I learnt how to use and when to use TPL while implementing this concept for sample application since INotifyDataErrorInfo can be done asynchronously.

History

XAML part styling has been added.

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