I have a need to get notified about validation errors that occur in a child collection of my ViewModel. For validation I use the INotifyDataErrorInfo interface.I've created a class that handles this successfully however, I feel that additional refactoring / simplification can be done on it.Here's the class that implements validation and child collection validation tracking:public class Validatable : Observable, INotifyDataErrorInfo{    [XmlIgnore]    private Dictionary<string, List<string>> errors =            new Dictionary<string, List<string>>();    [XmlIgnore]    // a collection that keeps all child tracked collections    public List<ICollection> TrackedCollections { get; set; } = new List<ICollection>();    [XmlIgnore]    public bool HasCollectionErrors { get; set; }    // method used by the view model    internal void RegisterCollection<T>(ObservableCollection<T> collection)    {        TrackedCollections.Add(collection);        collection.CollectionChanged += Collection_CollectionChanged;    }    // handle tracked collections changed events and registeres / unregisteres the error changed events    private void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)    {        if (e.NewItems != null)        {            foreach (var item in e.NewItems.OfType<Validatable>())            {                item.ErrorsChanged += Item_ErrorsChanged;            }        }        if (e.OldItems != null)        {            foreach (var item in e.OldItems.OfType<Validatable>())            {                item.ErrorsChanged -= Item_ErrorsChanged;            }        }    }    // notify parent about validation status change    private void Item_ErrorsChanged(object sender, DataErrorsChangedEventArgs e)    {        HasCollectionErrors = false;        foreach (var TrackedCollection in TrackedCollections)        {            foreach (var obj in TrackedCollection.OfType<Validatable>())            {                if (obj.HasErrors)                {                    HasCollectionErrors = true;                    break;                }            }        }        OnPropertyChanged(nameof(IsValid));    }    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;    public bool IsValid => !HasErrors && !HasCollectionErrors;    public bool HasErrors => errors.Any();    public void OnErrorsChanged(string propertyName)    {        ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));    }    public IEnumerable GetErrors(string propertyName)    {        if (string.IsNullOrWhiteSpace(propertyName)) return null;        if (errors.ContainsKey(propertyName) &&            errors[propertyName] != null &&            errors[propertyName].Count > 0)        {            return errors[propertyName];        }        return null;    }    public void Validate()    {        ClearErrors();        var results = new List<ValidationResult>();        var context = new ValidationContext(this);        Validator.TryValidateObject(this, context, results, true);        if (results.Any())        {            var propertyNames = results.SelectMany(m => m.MemberNames).Distinct().ToList();            foreach (var propertyName in propertyNames)            {                errors[propertyName] = results                   .Where(m => m.MemberNames.Contains(propertyName))                   .Select(r => r.ErrorMessage)                   .Distinct()                   .ToList();                OnErrorsChanged(propertyName);            }        }        OnPropertyChanged(nameof(IsValid));    }    protected void ClearErrors()    {        foreach (var propertyName in errors.Keys.ToList())        {            errors.Remove(propertyName);            OnErrorsChanged(propertyName);        }    }}Observable class:public class Observable : INotifyPropertyChanged{    public event PropertyChangedEventHandler PropertyChanged;    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)    {        var handler = PropertyChanged;        if (handler != null)        {            handler(this, new PropertyChangedEventArgs(propertyName));        }    }    protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName]string propertyName = null)    {        if (Equals(member, val)) return;        member = val;        if (PropertyChanged != null)            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));    }}Sample View Model:public class Person : Validatable{    private string _name;    public Person()    {        Addresses = new ObservableCollection<Address>();        RegisterCollection(Addresses);    }    [Required]    public string Name    {        get { return _name; }        set        {            SetProperty(ref _name, value);            Validate();        }    }    public ObservableCollection<Address> Addresses { get; private set; }}public class Address : Validatable{    private string _street;    [Required]    public string Street    {        get { return _street; }        set        {            SetProperty(ref _street, value);            Validate();        }    }}
WPF Track child collection validation errors
c#;validation;wpf;mvvm