Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObservableCollection and Item PropertyChanged

I've seen lots of talk about this question but maybe I'm just too much of a newbie to get it. If I have an observable collection that is a collection of "PersonNames" as in the msdn example (http: //msdn.microsoft.com/en-us/library/ms748365.aspx), I get updates to my View if a PersonName is added or removed, etc. I want to get an update to my View when I change a property in the PersonName as well. Like if I change the first name. I can implement OnPropertyChanged for each property and have this class derive from INotifyPropertyChanged and that seems to get called as expected.

My question is, how does the View get the updated data from the ObservableCollection as the property changed does not cause any event for the ObservableCollection?

This is probably something really simple but why I can't seem to find an example surprises me. Can anyone shed any light on this for me or have any pointers to examples I would greatly appreciate it. We have this scenario in multiple places in our current WPF app and are struggling with figuring it out.


"Generally, the code responsible for displaying the data adds a PropertyChanged event handler to each object currently displayed onscreen."

Could someone please give me an example of what this means? My View binds to my ViewModel which has a ObservableCollection. This collection is made up of a RowViewModel which has properties that support the PropertiesChanged event. But I can't figure out how to make the collection update itself so my view will be updated.

like image 356
Bill Campbell Avatar asked May 23 '09 16:05

Bill Campbell


People also ask

What is the difference between ObservableCollection and BindingList?

The true difference is rather straightforward: ObservableCollection<T> implements INotifyCollectionChanged which provides notification when the collection is changed (you guessed ^^) It allows the binding engine to update the UI when the ObservableCollection is updated. However, BindingList<T> implements IBindingList .

What is Propertychanged?

Property-change events occur whenever the value of a bound property changes for a bean — a component that conforms to the JavaBeans™ specification. You can find out more about beans from the JavaBeans trail of the Java Tutorial. All Swing components are also beans.

Does ObservableCollection implement INotifyPropertyChanged?

INotifyCollectionChanged . ObservableCollection implements INotifyCollectionChanged which will notify us when the collection has a mutated state.

What is the use of INotifyPropertyChanged in C#?

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed. For example, consider a Person object with a property called FirstName .


2 Answers

Here is how you would attach/detach to each item's PropertyChanged event.

ObservableCollection<INotifyPropertyChanged> items = new ObservableCollection<INotifyPropertyChanged>(); items.CollectionChanged += items_CollectionChanged;  static void items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {     if (e.OldItems != null)     {         foreach (INotifyPropertyChanged item in e.OldItems)             item.PropertyChanged -= item_PropertyChanged;     }     if (e.NewItems != null)     {         foreach (INotifyPropertyChanged item in e.NewItems)             item.PropertyChanged += item_PropertyChanged;     } }  static void item_PropertyChanged(object sender, PropertyChangedEventArgs e) {     throw new NotImplementedException(); } 
like image 99
chilltemp Avatar answered Oct 01 '22 03:10

chilltemp


We wrote this in the WPF-chat:

public class OcPropertyChangedListener<T> : INotifyPropertyChanged where T : INotifyPropertyChanged {     private readonly ObservableCollection<T> _collection;     private readonly string _propertyName;     private readonly Dictionary<T, int> _items = new Dictionary<T, int>(new ObjectIdentityComparer());     public OcPropertyChangedListener(ObservableCollection<T> collection, string propertyName = "")     {         _collection = collection;         _propertyName = propertyName ?? "";         AddRange(collection);         CollectionChangedEventManager.AddHandler(collection, CollectionChanged);     }      private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)     {         switch (e.Action)         {             case NotifyCollectionChangedAction.Add:                 AddRange(e.NewItems.Cast<T>());                 break;             case NotifyCollectionChangedAction.Remove:                 RemoveRange(e.OldItems.Cast<T>());                 break;             case NotifyCollectionChangedAction.Replace:                 AddRange(e.NewItems.Cast<T>());                 RemoveRange(e.OldItems.Cast<T>());                 break;             case NotifyCollectionChangedAction.Move:                 break;             case NotifyCollectionChangedAction.Reset:                 Reset();                 break;             default:                 throw new ArgumentOutOfRangeException();         }      }      private void AddRange(IEnumerable<T> newItems)     {         foreach (T item in newItems)         {             if (_items.ContainsKey(item))             {                 _items[item]++;             }             else             {                 _items.Add(item, 1);                 PropertyChangedEventManager.AddHandler(item, ChildPropertyChanged, _propertyName);             }         }     }      private void RemoveRange(IEnumerable<T> oldItems)     {         foreach (T item in oldItems)         {             _items[item]--;             if (_items[item] == 0)             {                 _items.Remove(item);                 PropertyChangedEventManager.RemoveHandler(item, ChildPropertyChanged, _propertyName);             }         }     }      private void Reset()     {         foreach (T item in _items.Keys.ToList())         {             PropertyChangedEventManager.RemoveHandler(item, ChildPropertyChanged, _propertyName);             _items.Remove(item);         }         AddRange(_collection);     }      public event PropertyChangedEventHandler PropertyChanged;      protected virtual void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)     {         PropertyChangedEventHandler handler = PropertyChanged;         if (handler != null)             handler(sender, e);     }      private class ObjectIdentityComparer : IEqualityComparer<T>     {         public bool Equals(T x, T y)         {             return object.ReferenceEquals(x, y);         }         public int GetHashCode(T obj)         {             return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);         }     } }  public static class OcPropertyChangedListener {     public static OcPropertyChangedListener<T> Create<T>(ObservableCollection<T> collection, string propertyName = "") where T : INotifyPropertyChanged     {         return new OcPropertyChangedListener<T>(collection, propertyName);     } } 
  • Weak events
  • Keeps track of the same item being added multiple times to the collection
  • It ~bubbles~ up the property changed events of the children.
  • The static class is just for convenience.

Use it like this:

var listener = OcPropertyChangedListener.Create(yourCollection); listener.PropertyChanged += (sender, args) => { //do you stuff} 
like image 33
Johan Larsson Avatar answered Oct 01 '22 03:10

Johan Larsson