In the .Net Framework 4.5 Windows Presentation Foundation (WPF) introduced some new features and improvements in various areas.
One new interface is introduced named “InotifyDataErrorInfo” that can be found in “System.ComponentModel.INotifyDataErrorInfo ” that supports synchronous and asynchronous data validation.
- Namespace: System.ComponentModel.
- Assembly: System (in System.dll).
Note: It can also be found in Silverlight since version 4.
The INotifyDataErrorInfo interface enables data entity classes to implement custom validation rules and expose validation results asynchronously.
InotifyDataErrorInfo also supports:
• Custom error objects
• Multiple errors per property
• Cross-property errors
• Entity-level errors
Figure 1
Figure 2
Figure 3
GetError MethodIt returns the IEnumerable object that contains the validation errors for any specific property or all properties.
Error Change Event
Always raise the ErrorChange event whenever the collection is returned by the GetError method.
Note
If the source of a two-way data binding implements the INotifyDataErrorInfo interface and the “ValidatesOnNotifyDataErrors” property of the binding is set to true (which it is by default), the WPF 4.5 binding engine automatically monitors the “ErrorsChanged” event and calls the “GetErrors” method to retrieve the updated errors once the event is raised from the source object, provided that the “HasErrors” property returns true.
Let's have a quick example to see how this interface works in WPF 4.5
Objective
The user needs to validate the Username, Email and Re-type email on user interface (WPF).
Code
- public class UserInput : ValidatableModel
- {
- private string _userName;
- private string _email;
- private string _repeatEmail;
-
- [Required]
- [StringLength(20)]
- public string UserName
- {
- get { return _userName; }
- set { _userName = value; RaisePropertyChanged("UserName"); }
- }
-
- [Required]
- [EmailAddress]
- [StringLength(60)]
- public string Email
- {
- get { return _email; }
- set { _email = value; RaisePropertyChanged("Email"); }
- }
-
- [Required]
- [EmailAddress]
- [StringLength(60)]
- [CustomValidation(typeof(UserInput), "SameEmailValidate")]
- public string RepeatEmail
- {
- get { return _repeatEmail; }
- set { _repeatEmail = value; RaisePropertyChanged("RepeatEmail"); }
- }
-
- public static ValidationResult SameEmailValidate(object obj, ValidationContext context)
- {
- var user = (UserInput)context.ObjectInstance;
- if (user.Email != user.RepeatEmail)
- {
- return new ValidationResult("The emails are not equal", new List<string> { "Email", "RepeatEmail" });
- }
- return ValidationResult.Success;
- }
- }
-
-
-
-
- public class ValidatableModel : INotifyDataErrorInfo, INotifyPropertyChanged
- {
- private ConcurrentDictionary<string, List<string>> _errors = new ConcurrentDictionary<string, List<string>>();
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- public void RaisePropertyChanged(string propertyName)
- {
- var handler = PropertyChanged;
- if (handler != null)
- handler(this, new PropertyChangedEventArgs(propertyName));
- ValidateAsync();
- }
-
- public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
-
- public void OnErrorsChanged(string propertyName)
- {
- var handler = ErrorsChanged;
- if (handler != null)
- handler(this, new DataErrorsChangedEventArgs(propertyName));
- }
-
- public IEnumerable GetErrors(string propertyName)
- {
- List<string> errorsForName;
- _errors.TryGetValue(propertyName, out errorsForName);
- return errorsForName;
- }
-
- public bool HasErrors
- {
- get { return _errors.Any(kv => kv.Value != null && kv.Value.Count > 0); }
- }
-
- public Task ValidateAsync()
- {
- return Task.Run(() => Validate());
- }
-
- private object _lock = new object();
- public void Validate()
- {
- lock (_lock)
- {
- var validationContext = new ValidationContext(this, null, null);
- var validationResults = new List<ValidationResult>();
- Validator.TryValidateObject(this, validationContext, validationResults, true);
-
- foreach (var kv in _errors.ToList())
- {
- if (validationResults.All(r => r.MemberNames.All(m => m != kv.Key)))
- {
- List<string> outLi;
- _errors.TryRemove(kv.Key, out outLi);
- OnErrorsChanged(kv.Key);
- }
- }
-
- var q = from r in validationResults
- from m in r.MemberNames
- group r by m into g
- select g;
-
- foreach (var prop in q)
- {
- var messages = prop.Select(r => r.ErrorMessage).ToList();
-
- if (_errors.ContainsKey(prop.Key))
- {
- List<string> outLi;
- _errors.TryRemove(prop.Key, out outLi);
- }
- _errors.TryAdd(prop.Key, messages);
- OnErrorsChanged(prop.Key);
- }
- }
- }
- }
OutputMove the cursor to the Username, Email and Re-type email TextBoxes.
Re-type the wrong email.