Search code examples
c#observablecollectioninotifypropertychanged

ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)


Does anyone know why this code doesn't work:

public class CollectionViewModel : ViewModelBase {  
    public ObservableCollection<EntityViewModel> ContentList
    {
        get { return _contentList; }
        set 
        { 
            _contentList = value; 
            RaisePropertyChanged("ContentList"); 
            //I want to be notified here when something changes..?
            //debugger doesn't stop here when IsRowChecked is toggled
        }
     }
}

public class EntityViewModel : ViewModelBase
{

    private bool _isRowChecked;

    public bool IsRowChecked
    {
        get { return _isRowChecked; }
        set { _isRowChecked = value; RaisePropertyChanged("IsRowChecked"); }
    }
}

ViewModelBase containts everything for RaisePropertyChanged etc. and it's working for everything else except this problem..


Solution

  • The ContentList's Set method will not get called when you change a value inside the collection, instead you should be looking out for the CollectionChanged event firing.

    public class CollectionViewModel : ViewModelBase
    {          
        public ObservableCollection<EntityViewModel> ContentList
        {
            get { return _contentList; }
        }
    
        public CollectionViewModel()
        {
             _contentList = new ObservableCollection<EntityViewModel>();
             _contentList.CollectionChanged += ContentCollectionChanged;
        }
    
        public void ContentCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            //This will get called when the collection is changed
        }
    }
    

    Okay, that's twice today I've been bitten by the MSDN documentation being wrong. In the link I gave you it says:

    Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.

    But it actually doesn't fire when an item is changed. I guess you'll need a more bruteforce method then:

    public class CollectionViewModel : ViewModelBase
    {          
        public ObservableCollection<EntityViewModel> ContentList
        {
            get { return _contentList; }
        }
    
        public CollectionViewModel()
        {
             _contentList = new ObservableCollection<EntityViewModel>();
             _contentList.CollectionChanged += ContentCollectionChanged;
        }
    
        public void ContentCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach(EntityViewModel item in e.OldItems)
                {
                    //Removed items
                    item.PropertyChanged -= EntityViewModelPropertyChanged;
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach(EntityViewModel item in e.NewItems)
                {
                    //Added items
                    item.PropertyChanged += EntityViewModelPropertyChanged;
                }     
            }       
        }
    
        public void EntityViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            //This will get called when the property of an object inside the collection changes
        }
    }
    

    If you are going to need this a lot you may want to subclass your own ObservableCollection that triggers the CollectionChanged event when a member triggers its PropertyChanged event automatically (like it says it should in the documentation...)