WPF Validation: Part 1

Introduction

I do not want to waste time on unrelated things, such as how I endured the hard times when this concept was given to me for use in my project. Now I really satisfied myself because it motivated me to write this topic. This article would be more useful for WPF novices.

Basically we can do the data validation in one of the following two ways.

  • Part 1: Implements IDataErrorInfo
  • Part 2: Implements INotifyDataErrorInfo

Now I would like to give you the idea in Part 1 sample project implementation. We will see Part 2 implementation in the future article.

Step 1

Create a project (WpfValidation) and then create a class file named “Customer.cs”.

Step 2

In the Customer.cs file, create the “Name” property as given below. If you want to use some logic in the name validation then use one private name backing field along with a public property. Here I do not want to have any additional logic for the Name property. So I simply use the Public property only.

  1. public string Name  
  2.    {  
  3.       get;  
  4.       set;  
  5.    }  
Step 3

Implement an “IDataErrorInfo” interface in the Customer.cs file. Add a using for the "System.ComponentModel" namespace (as in “using System.ComponentModel”). This interface gives us two properties, those are Error and this. Change those two properties as below.
  1. public string Error  
  2.    {  
  3.       get { return null; }  
  4.    }  
  5.   
  6. public string this[string columnName]  
  7.    {  
  8.       get  
  9.    {  
  10.          string result = string.Empty;  
  11.          if (columnName == "Name")  
  12.             {  
  13.                if (this.Name == "")  
  14.                   result = "Name can not be empty";  
  15.             }  
  16.       return result;  
  17.    }  
  18. }  
Define / assign your message that you want to display when the data goes wrong. Here I used a result variable to store the error message. Finally our Customer class looks such as shown below.
  1. public class Customer:IDataErrorInfo  
  2. {  
  3.     public string Name  
  4.     {  
  5.         get;  
  6.         set;  
  7.     }          
  8.   
  9.     public string Error  
  10.     {  
  11.         get { return null; }  
  12.     }  
  13.   
  14.     public string this[string columnName]  
  15.     {  
  16.         get  
  17.         {  
  18.             string result = string.Empty;  
  19.             if (columnName == "Name")  
  20.             {  
  21.                 if (this.Name == "")  
  22.                     result = "Name can not be empty";  
  23.             }  
  24.             return result;  
  25.         }  
  26.     }  
  27. }  
That's all in the code behind. Now we can move into the XAML part.

Now open MainWindow.xaml. Just create two TextBoxes with the necessary settings.
  • I created two text boxes inside the grid like the following.
  1. <TextBox HorizontalAlignment="Left" x:Name="SampleTextBox" Height="35" Width="150" />  
  2. <TextBox HorizontalAlignment="Right" x:Name="eTextBox" Height="35" Width="150" Text="Super" />  
  • Create a style in the resources section for easy readability. Otherwise you can have the style property definition inside <Textbox.Style></Textbox.Style>.

I always prefer the first way, in other words, inside the resources section to reuse the style wherever we want.

  1. <Style x:Key="TextErrorStyle" TargetType="{x:Type TextBox}"> </Style>  
Explanation
 
If suppose we do have an error in the text box, then what are all things we want to notify the user that something went wrong. (Basically a tooltip and changing the background color of the TextBox).
This style will apply only if “Validation.HasError” returns true. So we should specify it in the Trigger property.
  1. <Style x:Key="TextErrorStyle" TargetType="{x:Type TextBox}">  
  2.     <Style.Triggers>  
  3.         <Trigger Property="Validation.HasError" Value="True">             
  4.             <Setter Property="Background" Value="Red"/>  
  5.             <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors)[0].ErrorContent}"></Setter>  
  6.         </Trigger>  
  7.     </Style.Triggers>  
  8. </Style>  
Here we should understand about relative source that will get the values from the TextBox control (here SampleTextBox) by specifying RelativeSource.Self (that control uses this style, in other words TextErrorStyle). We will get all the values of that control then which property we're specifying in Path property will be assigned to the ToolTip.
 
If suppose I want to draw a Red color over the textbox rather than changing the background color. For that we should define the “Validation.ErrorTemplate” property as below.
  1. <Setter Property="Validation.ErrorTemplate">  
  2.     <Setter.Value>  
  3.         <ControlTemplate x:Name="TextErrorTemplate">  
  4.             <DockPanel>  
  5.                 <Border BorderBrush="Red" BorderThickness="1">  
  6.                     <AdornedElementPlaceholder/>  
  7.                 </Border>  
  8.                 <TextBlock FontSize="20" Foreground="Red">*?*</TextBlock>  
  9.             </DockPanel>  
  10.         </ControlTemplate>  
  11.     </Setter.Value>  
  12. </Setter>  
Note

<AdornedElementPlaceholder/> is our control, here “SampleTextBox”, based upon this we should place our exclamation or question mark. Basically we cannot hold two more controls without a container (here DockPanel) in <ControlTemplate /> , but If our requirement is to show only one control then we can ignore the container.

That's all about style. Now we can move to attaching defined properties to the “SampleTextBox” Control.
  • I bound all the properties to the text box control as below. We need to create an instance of the Customer class. We can follow either in code behind or XAML itself. I used it to create in XAML as in the following.

Import the solution along with the project namespace in XAML.   

  1. xmlns:local="clr-namespace:WpfValidation"  
  2. <Window.Resources>  
  3.     <local:Customer x:Key="CustomerInstance" Name="Srini"/>  
  4. </Window.Resources>   
Here we can pass the default value for our created Name property.
  1. <TextBox HorizontalAlignment="Left" x:Name="SampleTextBox" Height="35" Width="150" Style="{StaticResource TextErrorStyle}">  
  2.     <TextBox.Text>  
  3.         <Binding Path="Name" Source="{StaticResource CustomerInstance}" UpdateSourceTrigger="PropertyChanged" >              
  4.         </Binding>  
  5.     </TextBox.Text>  
  6. </TextBox>  
Here I used “UpdateSourceTrigger = PropertyChanged” since I want to get notification whenever keys are entered or deleted. Still it is not over. Two more things are remaining to satisfy our requirements.
  • We have now used IDataErrorInfo in this project, so we need to use ValidatesOnDataErrors =”True”.
        
  • Finally, ValidationRules needs to be set. DataErrorValidationRule, ExceptionValidationRule, NotifyDataErrorValidationRule are there. I need to throw an exception if my source property is updated.
  1. <TextBox HorizontalAlignment="Left" x:Name="SampleTextBox" Height="35" Width="150">  
  2.     <TextBox.Text>  
  3.         <Binding Path="Name" Source="{StaticResource CustomerInstance}" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged">  
  4.             <Binding.ValidationRules>  
  5.                 <ExceptionValidationRule></ExceptionValidationRule>  
  6.             </Binding.ValidationRules>  
  7.         </Binding>  
  8.     </TextBox.Text>  
  9. </TextBox>  
The rest will be in the Part 2 article.