Here is the first post of a serie about the new features of WPF 4.5. Validation of data is often if not always necessary in modern applications. From a long time, WPF provided the IDataErrorInfo interfaces which permitted the automatic validation of your properties.
Silverlight, with is asynchronous philosophy provided the INotifyDataErrorInfo which performed the same thing but asyncrhonously.
It is a newinterface of WPF 4.5 and we will discover it in this post.
What’s inside ?
As you can see there is only 3 things inside:
- HasErrors: a read-only boolean property which tells if the object as a whole have any validation errors;
- GetErrors: a method which returns validation errors for a given property;
- ErrorsChanged: an event which must be raised when new errors – or the lacks of errors – is detected. You have to raise this event for each property.
As a note, if you return false in the HasErrors property, the binding will act as if there were no errors, even if they exists.
How to use it ?
With the traditionnal IDataErrorInfo, you have to set to true the ValidatesOnDataErrors property on each binding to your object. There is nothing really new under the sun because this time you have to set the ValidatesOnNotifyDataErrors property to true.
In the linked demo project I create a form which display the properties of an object named ‘Person’. Here is how the validation with INotifyDataErrorInfo is enabled in the Binding:
[xml]
The binding will then register itself for the ErrorsChanged event of the binded Person. Eeach time this event is raised for the binded property, the controls will dress itself to display an error. As pointed out before, this is done only if the HasErrors is set to true.
I have implemented a delay in the validation of the Person class. The validations occured after each set of any properties but it is delayed by ‘WaitSecondsBeforeValidation’ seconds.
I also maintain a dictionary of all the errors by property name and use it to provide them via the GetErros methods. The HasErrors property returns true if this any error exists in this dictionary. Here is the code for this basic implementation:
[csharp]public System.Collections.IEnumerable GetErrors(string propertyName)
{
List
_errors.TryGetValue(“Name”, out errorsForName);
return errorsForName;
}
public bool HasErrors
{
get { return _errors.Values.FirstOrDefault(l => l.Count > 0) != null; }
}
private Dictionary
new Dictionary
private void Validate()
{
Task waitTask = new Task(() => Thread.Sleep(
TimeSpan.FromSeconds(WaitSecondsBeforeValidation)));
waitTask.ContinueWith((_) => RealValidation());
waitTask.Start();
}
private object _lock = new object();
private void RealValidation()
{
lock (_lock)
{
//Validate Name
List
if (!_errors.TryGetValue(“Name”, out errorsForName))
errorsForName = new List
else errorsForName.Clear();
if (String.IsNullOrEmpty(Name))
errorsForName.Add(“The name can’t be null or empty.”);
_errors[“Name”] = errorsForName;
if (errorsForName.Count > 0) RaiseErrorsChanged(“Name”);
//Validate Age
List
if (!_errors.TryGetValue(“Age”, out errorsForAge))
errorsForAge = new List
else errorsForAge.Clear();
if (Age <= 0)
errorsForAge.Add("The age must be greater than zero.");
_errors["Age"] = errorsForAge;
if (errorsForAge.Count > 0) RaiseErrorsChanged(“Age”);
}
[/csharp]
Finally, I have created a RaiseErrorsChanged method which ease the raising of Validations event errors:
[csharp] public event EventHandler
public void RaiseErrorsChanged(string propertyName)
{
EventHandler
if (handler == null) return;
var arg = new DataErrorsChangedEventArgs(propertyName);
handler.Invoke(this, arg);
}
[/csharp]
Demo application
The demo application can be found on this Dropbox folder. Don’t forget to register yourself on dropbox using this link 🙂.
You can use the generate errors button to unvalidated the binded field and configure the delay to wait before the validation trough the interface:
Regards,
09/11/2011 16 h 20 min
You can simplify your HasErrors property to:
return _errors.Values.Any(l => l.Count > 0);
Also, slight English nit-picking: something is bound, not binded. "Binded" isn't even a real word. 🙂
09/11/2011 16 h 22 min
Thank you, It is just a proof of concept, not "the best way to do it" but you are right 😉
16/03/2012 16 h 16 min
WPF + Async data validation: Finally WPF has a workable validation framework!
26/05/2012 16 h 11 min
You can also use ready made ErrorsContainer if you are using Prism http://msdn.microsoft.com/en-us/library/gg405531(…
16/01/2013 12 h 57 min
Thank you, It is just a proof of concept, not "the best way to do it" but you are right
25/09/2013 14 h 13 min
Who should call the private void Validate()?
07/10/2013 10 h 26 min
The entity should do it when needed 🙂
08/10/2013 14 h 35 min
Hello, the link for demo project is broken 😉
25/07/2015 12 h 33 min
The link is now available here : https://www.dropbox.com/s/pp70lm8lnc0vmru/WPF4.5_…
02/11/2013 18 h 16 min
Dropbox link for POC is broken.
25/01/2016 16 h 57 min
Hi, the GetErrors(string propName) implementation is incomplete because you did not handle the null (msdn states that GetErrors might be called using a valid property name, an empty string or null). If the framework calls your method with null you'll get an exception.