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 string
s for one property. Those defined validation string
s 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.