Search code examples
c#inotifycollectionchanged

CollectionChanged - Name of Collection


I have implemented CollectionChanged for an ObservableCollection just like here: Implementing CollectionChanged

Is there a possibility in the OnCollectionChanged method to find out what the name of the changed property is?

EDIT: A history should be written if there is any change in the class. There are three cases I want to achieve:

  1. "Normal" property is changed (string, int, ...): this is already working
  2. Add and remove in collections: would be working if I know the name of the changed collection
  3. Property inside a collection is changed: same problem as 2) I don't know the name (and index) of the ch

    public class ParentClass : BaseModel
    {
        public ParentClass()
        {
            Children = new ObservableCollection<SomeModel>();
            Children.CollectionChanged += Oberservable_CollectionChanged;
        }
    
        private string id;
    
        public string Id
        {
            get
            {
                return id;
            }
            set
            {
                string oldId = id;
                id = value;
                OnPropertyChanged(oldArticleId,id);
            }
        }
    
        public ObservableCollection<SomeModel> Children { get; set; }
    
        protected virtual void OnPropertyChanged(object oldValue, object newValue, [CallerMemberName] string propertyName = null)
        {
            //1) Entry is added to history (this part is working)
        }
    
    
        protected void Oberservable_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (INotifyPropertyChanged added in e.NewItems)
                {
                    added.PropertyChanged += OnPropertyChangedHandler;
                    OnCollectionChanged(CollectionChangedModel.Action.Added, added);
                }
            }
    
            if (e.OldItems != null)
            {
                foreach (INotifyPropertyChanged removed in e.OldItems)
                {
                    removed.PropertyChanged -= OnPropertyChangedHandler;
                    OnCollectionChanged(CollectionChangedModel.Action.Removed, removed);
                }
            }
        }
    
        protected virtual void OnCollectionChanged(CollectionChangedModel.Action action, object value)
        {
            //2) TODO: History should be written, but I don't have the property name
        }
    
        public void OnPropertyChangedHandler(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
        {
            //3) TODO: History about changed property in child-class (not working because of missing property name and index)
        }
    }
    

Solution

  • This is correct:

    Children = new ObservableCollection<SomeModel>();
    Children.CollectionChanged += Oberservable_CollectionChanged;
    

    Though you have to ensure nobody will change collection, e.g. like this:

    public ObservableCollection<SomeModel> Children { get; }
    

    or rather making it a full property and subscribing/unsubscribing in the setter.

    And now regarding Oberservable_CollectionChanged handler:

    1. Add and remove in collections: would be working if I know the name of the changed collection

    Add sender to your event. Wrap arguments into ...EventArgs class (make it immutable), see msdn.

    public event EventHandler<MyCollectionChangedEventArgs> CollectionChanged;
    protected virtual void OnCollectionChanged(object sender, MyCollectionChangedEventArgs e)
    {
        //2) TODO: History should be written, but I don't have the property name
    
        // sender is name
        // e.Action and e.Value are parameters
    }
    
    1. Property inside a collection is changed: same problem as 2) I don't know the name (and index) of the ch

    Wrap event handlers into instance containing event handler and value you need (I am leaving that task for you). If unsubscribing is not required, this can be easily achieved by using lamdba closures:

    foreach (var added in e.NewItems.OfType<INotifyPropertyChanged>)
    {
        added.PropertyChanged += (s, e) => 
        {
            // you have `sender` here in addition to `s`, clever?
        };
    }