Notify ObservableCollection when Item changes
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();
}
Notify item changes in observable collection in WPF/C#
You should implement it yourself, my example of observable collection (and also you need to subscribe and Raise OnPropertyChanged(nameof(Total))) when collection item was changed, or change my implementation of collectionEx to raising collection changed event.
public class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
public ObservableCollectionEx(IEnumerable<T> initialData) : base(initialData)
{
Init();
}
public ObservableCollectionEx()
{
Init();
}
private void Init()
{
foreach (T item in Items)
item.PropertyChanged += ItemOnPropertyChanged;
CollectionChanged += FullObservableCollectionCollectionChanged;
}
private void FullObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (T item in e.NewItems)
{
if (item != null)
item.PropertyChanged += ItemOnPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (T item in e.OldItems)
{
if (item != null)
item.PropertyChanged -= ItemOnPropertyChanged;
}
}
}
private void ItemOnPropertyChanged(object sender, PropertyChangedEventArgs e)
=> ItemChanged?.Invoke(sender, e);
public event PropertyChangedEventHandler ItemChanged;
}
How to make an observableCollection notify when an item is updated and not only when is added or removed?
ObservableCollection
purpose are to notify the change of a collection, to notify the modification of an object you must implement INotifyPropertyChanged
in the object contained in the collection.
ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)
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...)
how to get the notification on change in ObservableCollection object
There is only really one way to do this: hook up an event handler on each item as (or before) you add it to the ObservableCollection.
notifyme.Add(new Notify{ PropertyChanged += (o, e) => { do whatever }});
This is because the ObservableCollection is simply a container, each item in it has to be individually hooked up. Of course you could write your own extending class (or extension method) that helps automate this.
How can I notify that a property of an item in an observable collection has changed?
You should be able to tell the View that the collection has changed, and in turn, trigger it to rebind to the entire collection (which would update the View).
If your Model implements INotifyPropertyChanged
, the other option would be to have your ViewModelB
class listen for changes on it's wrapped Model, and raise property changed events as needed.
ObservableCollection that fires when containing items change
Your TextBlock that bound to HolmList[0].IsOnline
didn't update because IsOnline
on Holm
didn't notify that its value changed.
You can listen to TestSensor
's PropertyChanged
event in TestSensor
and notify IsOnline
property change when one of TestSensor
's IsOnline
property change.
class Holm : ModelBase
{
public Holm(String Name, TestSensor sensor1, TestSensor sensor2)
{
Sensor1 = sensor1;
Sensor2 = sensor2;
this.Name = Name;
Sensor1.PropertyChanged += OnSensorOnlineChanged;
Sensor2.PropertyChanged += OnSensorOnlineChanged;
}
private void OnSensorOnlineChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsOnline")
{
OnPropertyChanged(nameof(IsOnline));
}
}
}
The nameof keyword
Related Topics
Generics and Casting - Cannot Cast Inherited Class to Base Class
Why Is a Dictionary "Not Ordered"
Random Number Generator with No Duplicates
C# Object Pooling Pattern Implementation
A Generic List of Anonymous Class
Get All Derived Types of a Type
Displayname Attribute from Resources
What's the Difference Between Sortedlist and Sorteddictionary
Multiple Cases in Switch Statement
Casting a Variable Using a Type Variable
Xml Serialization - Disable Rendering Root Element of Array
Write a Well Designed Async/Non-Async API
How to Set Aspnetcore_Environment to Be Considered for Publishing an ASP.NET Core Application