From WPF 4.5, Microsoft added support of INotifyDataErrorInfo validation from WPF. The model we want to validate now can implement this interface and can do multiple validations for the property. It also supports aync validation. The way you want to implement varies by requirements. There are so many options with this. In this article, I will provide a simpler version. I am mostly a web developer, so I always try maintain a common standard for developing both WPF/Silverlight/MVC. You can choose your own.
INotifyDataErrorInfo Interface
It has only three members, all are self-explanatory:
- public interface INotifyDataErrorInfo
- {
- bool HasErrors { get; }
- event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
- IEnumerable GetErrors(string propertyName);
- }
The INotifyDataErrorInfo interface rovides us more flexibility on model validation. We can decide when we want to validate properties, for example in property setters. We can signal errors on single properties as well as cross-property errors and model level errors. In this example I will use an annotation type validation trigger.
Validation Base
Please go through the following implementation:
- public class ValidationBase : INotifyDataErrorInfo
- {
- private Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
- public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
- private object _lock = new object();
- public bool HasErrors { get { return _errors.Any(propErrors => propErrors.Value != null && propErrors.Value.Count > 0); } }
- public bool IsValid { get { return this.HasErrors; } }
- public IEnumerable GetErrors(string propertyName)
- {
- if (!string.IsNullOrEmpty(propertyName))
- {
- if (_errors.ContainsKey(propertyName) && (_errors[propertyName] != null) && _errors[propertyName].Count > 0)
- return _errors[propertyName].ToList();
- else
- return null;
- }
- else
- return _errors.SelectMany(err => err.Value.ToList());
- }
- public void OnErrorsChanged(string propertyName)
- {
- if (ErrorsChanged != null)
- ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
- }
- public void ValidateProperty(object value, [CallerMemberName] string propertyName = null)
- {
- lock (_lock)
- {
- var validationContext = new ValidationContext(this, null, null);
- validationContext.MemberName = propertyName;
- var validationResults = new List<ValidationResult>();
- Validator.TryValidateProperty(value, validationContext, validationResults);
- //clear previous _errors from tested property
- if (_errors.ContainsKey(propertyName))
- _errors.Remove(propertyName);
- OnErrorsChanged(propertyName);
- HandleValidationResults(validationResults);
- }
- }
- public void Validate()
- {
- lock (_lock)
- {
- var validationContext = new ValidationContext(this, null, null);
- var validationResults = new List<ValidationResult>();
- Validator.TryValidateObject(this, validationContext, validationResults, true);
- //clear all previous _errors
- var propNames = _errors.Keys.ToList();
- _errors.Clear();
- propNames.ForEach(pn => OnErrorsChanged(pn));
- HandleValidationResults(validationResults);
- }
- }
- private void HandleValidationResults(List<ValidationResult> validationResults)
- {
- //Group validation results by property names
- var resultsByPropNames = from res in validationResults
- from mname in res.MemberNames
- group res by mname into g
- select g;
- //add _errors to dictionary and inform binding engine about _errors
- foreach (var prop in resultsByPropNames)
- {
- var messages = prop.Select(r => r.ErrorMessage).ToList();
- _errors.Add(prop.Key, messages);
- OnErrorsChanged(prop.Key);
- }
- }
- }
The same, except for the entire model, is done in the Validate method. Here I check all model properties at once. There can be alternative approach, but I am satisfied with this.
The Model Class
I used the Data Annotation technique to trigger the validation logic. Also, the model derives from ModelBase class that implements the INotifyPropertyChange interface for binding support.
- public class ModelBase : ValidationBase, INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
- protected void NotifyPropertyChanged(string propertyName)
- {
- if (this.PropertyChanged != null)
- {
- this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
- }
- }
- }
- public class Customer : ModelBase
- {
- private string _firstName;
- [Display(Name="First Name")]
- [Required]
- [StringLength(20)]
- public string FirstName
- {
- get { return _firstName; }
- set
- {
- _firstName = value;
- ValidateProperty(value);
- base.NotifyPropertyChanged("FirstName");
- }
- }
- }
View
The view is quite simple. Bind the data context to your view model and then place the binding. But you need to include three attributes in the Binding:
- NotifyOnValidationError=True,ValidatesOnNotifyDataErrors=True,UpdateSourceTrigger=PropertyChanged
- <TextBox Text="{Binding Customer.FirstName,Mode=TwoWay, NotifyOnValidationError=True,ValidatesOnNotifyDataErrors=True,UpdateSourceTrigger=PropertyChanged}">
- <Validation.ErrorTemplate>
- <ControlTemplate>
- <StackPanel>
- <!-- Placeholder for the TextBox itself -->
- <AdornedElementPlaceholder x:Name="textBox"/>
- <ItemsControl ItemsSource="{Binding}">
- <ItemsControl.ItemTemplate>
- <DataTemplate>
- <TextBlock Text="{Binding ErrorContent}" Foreground="Red"/>
- </DataTemplate>
- </ItemsControl.ItemTemplate>
- </ItemsControl>
- </StackPanel>
- </ControlTemplate>
- </Validation.ErrorTemplate>
- </TextBox>

Rod FalangaPosted Mar 16, 2021, 4:53 PM
Hi Arunava, do you have an example which uses this code samples you have in this article?
HenryPosted Oct 3, 2015, 7:32 PM
For anyone else having the same problem as Ashwin add a reference to System.ComponentModel.DataAnnotations for access to the Validation members.
Ashwin MasarkarPosted Mar 10, 2015, 1:18 AM
Thanks for the nice article. However, I'm facing bit of trouble in implementing the same in WPF. It looks like Validator class is not available in WPF and I'm not finding the best alternative of Validator class to be used in WPF.