Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Notify ObservableCollection when Item changes

I found on this link

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

some techniques to notify a Observablecollection that an item has changed. the TrulyObservableCollection in this link seems to be what i'm looking for.

public class TrulyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged {     public TrulyObservableCollection()     : base()     {         CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);     }      void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)     {         if (e.NewItems != null)         {             foreach (Object item in e.NewItems)             {                 (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);             }         }         if (e.OldItems != null)         {             foreach (Object item in e.OldItems)             {                 (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);             }         }     }      void item_PropertyChanged(object sender, PropertyChangedEventArgs e)     {         NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);         OnCollectionChanged(a);     } } 

But when I try to use it, I don't get notifications on the collection. I'm not sure how to correctly implement this in my C# Code:

XAML :

    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyItemsSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">         <DataGrid.Columns>             <DataGridCheckBoxColumn Binding="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>         </DataGrid.Columns>     </DataGrid> 

ViewModel :

public class MyViewModel : ViewModelBase {     private TrulyObservableCollection<MyType> myItemsSource;     public TrulyObservableCollection<MyType> MyItemsSource     {         get { return myItemsSource; }         set          {              myItemsSource = value;              // Code to trig on item change...             RaisePropertyChangedEvent("MyItemsSource");         }     }      public MyViewModel()     {         MyItemsSource = new TrulyObservableCollection<MyType>()         {              new MyType() { MyProperty = false },             new MyType() { MyProperty = true },             new MyType() { MyProperty = false }         };     } }  public class MyType : ViewModelBase {     private bool myProperty;     public bool MyProperty     {         get { return myProperty; }         set          {             myProperty = value;             RaisePropertyChangedEvent("MyProperty");         }     } }  public class ViewModelBase : INotifyPropertyChanged {     public event PropertyChangedEventHandler PropertyChanged;      protected void RaisePropertyChangedEvent(string propertyName)     {         if (PropertyChanged != null)         {             PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);             PropertyChanged(this, e);         }     } } 

When i run the program, i have the 3 checkbox to false, true, false as in the property initialisation. but when i change the state of one of the ckeckbox, the program go through item_PropertyChanged but never in MyItemsSource Property code.

like image 914
Pansoul Avatar asked Dec 13 '11 14:12

Pansoul


People also ask

How will an object be notified if the property bound to it has been changed?

Remarks. 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 .

What is the difference between ObservableCollection and list?

The true difference is rather straightforward:ObservableCollection 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 implements IBindingList.

What is ObservableCollection in C#?

An ObservableCollection is a dynamic collection of objects of a given type. Objects can be added, removed or be updated with an automatic notification of actions. When an object is added to or removed from an observable collection, the UI is automatically updated.


2 Answers

The spot you have commented as // Code to trig on item change... will only trigger when the collection object gets changed, such as when it gets set to a new object, or set to null.

With your current implementation of TrulyObservableCollection, to handle the property changed events of your collection, register something to the CollectionChanged event of MyItemsSource

public MyViewModel() {     MyItemsSource = new TrulyObservableCollection<MyType>();     MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;      MyItemsSource.Add(new MyType() { MyProperty = false });     MyItemsSource.Add(new MyType() { MyProperty = true});     MyItemsSource.Add(new MyType() { MyProperty = false }); }   void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {     // Handle here } 

Personally I really don't like this implementation. You are raising a CollectionChanged event that says the entire collection has been reset, anytime a property changes. Sure it'll make the UI update anytime an item in the collection changes, but I see that being bad on performance, and it doesn't seem to have a way to identify what property changed, which is one of the key pieces of information I usually need when doing something on PropertyChanged.

I prefer using a regular ObservableCollection and just hooking up the PropertyChanged events to it's items on CollectionChanged. Providing your UI is bound correctly to the items in the ObservableCollection, you shouldn't need to tell the UI to update when a property on an item in the collection changes.

public MyViewModel() {     MyItemsSource = new ObservableCollection<MyType>();     MyItemsSource.CollectionChanged += MyItemsSource_CollectionChanged;      MyItemsSource.Add(new MyType() { MyProperty = false });     MyItemsSource.Add(new MyType() { MyProperty = true});     MyItemsSource.Add(new MyType() { MyProperty = false }); }  void MyItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {     if (e.NewItems != null)         foreach(MyType item in e.NewItems)             item.PropertyChanged += MyType_PropertyChanged;      if (e.OldItems != null)         foreach(MyType item in e.OldItems)             item.PropertyChanged -= MyType_PropertyChanged; }  void MyType_PropertyChanged(object sender, PropertyChangedEventArgs e) {     if (e.PropertyName == "MyProperty")         DoWork(); } 
like image 60
Rachel Avatar answered Sep 27 '22 18:09

Rachel


I solved this case by using static Action


public class CatalogoModel  {     private String _Id;     private String _Descripcion;     private Boolean _IsChecked;      public String Id     {         get { return _Id; }         set { _Id = value; }     }     public String Descripcion     {         get { return _Descripcion; }         set { _Descripcion = value; }     }     public Boolean IsChecked     {         get { return _IsChecked; }         set         {            _IsChecked = value;             NotifyPropertyChanged("IsChecked");             OnItemChecked.Invoke();         }     }      public static Action OnItemChecked; }   public class ReglaViewModel : ViewModelBase {     private ObservableCollection<CatalogoModel> _origenes;      CatalogoModel.OnItemChecked = () =>             {                 var x = Origenes.Count;  //Entra cada vez que cambia algo en _origenes             }; } 
like image 24
sat1582 Avatar answered Sep 27 '22 18:09

sat1582