Created
November 17, 2013 00:04
-
-
Save itajaja/7507120 to your computer and use it in GitHub Desktop.
TrulyObservableCollection fires collectionchanged also when items change
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.ObjectModel; | |
using System.Collections.Specialized; | |
using System.ComponentModel; | |
namespace Hylasoft.OrdersGui.Utils | |
{ | |
public class TrulyObservableCollection<T> : ObservableCollection<T> | |
where T : INotifyPropertyChanged | |
{ | |
public TrulyObservableCollection() | |
{ | |
CollectionChanged += TrulyObservableCollection_CollectionChanged; | |
} | |
void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) | |
{ | |
if (e.NewItems != null) | |
{ | |
foreach (Object item in e.NewItems) | |
{ | |
(item as INotifyPropertyChanged).PropertyChanged += item_PropertyChanged; | |
} | |
} | |
if (e.OldItems != null) | |
{ | |
foreach (Object item in e.OldItems) | |
{ | |
(item as INotifyPropertyChanged).PropertyChanged -= item_PropertyChanged; | |
} | |
} | |
} | |
void item_PropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
var a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); | |
OnCollectionChanged(a); | |
} | |
} | |
} |
Your implementation is great and I did introduce new event too, at first. Then I discovered that this new event won't work with BindingOperations.EnableCollectionSynchronization(...), no queuing of those events on GUI thread would occur for example. This makes me feel that without some serious re-implementation the easiest solution is to inherit from NotifyCollectionChangedEventArgs and add "PropertyName" property. Next, use this new args class when rising CollectionChanged event and check for that type in the handlers.
public class CollectionItemPropertyChangedEventArgs : NotifyCollectionChangedEventArgs
{
public CollectionItemPropertyChangedEventArgs(NotifyCollectionChangedAction action, object newItem, object oldItem, int index, string itemPropertyName)
: base(action, newItem, oldItem, index)
{
PropertyName = itemPropertyName ?? throw new ArgumentNullException(nameof(itemPropertyName));
}
//
// Summary:
// Gets the name of the collection item's property that changed.
//
// Returns:
// The name of the collection item's property that changed.
public virtual string PropertyName { get; }
}
And when rising the event:
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new CollectionItemPropertyChangedEventArgs(
NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender), e.PropertyName);
OnCollectionChanged(args);
}
And in handler:
private void MappingEntryModel_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e is CollectionItemPropertyChangedEventArgs e1)
{
if (e1.PropertyName == "SomeProperty")
{
//deal with item property change
}
}
}
Do you have the latest version of this excellent source?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The implementation above suffers several drawbacks that does not make it suitable to use in a WPF MVVM scenario.
The problem is that, unluckily, there is not a 'NotifyCollectionChangedAction.ItemChanged' enum value to raise and we are forced to raise something else (Reset or Replace, etc) that does not really correspond to the actual change we made.
For example imagine you need to bind the ItemsSource of a TabControl to this implementation of TrulyObservableCollection.
Since a change on a value of an item of the collection equals to a change to the RESET of collection itself, the selection is lost on every change to the collection item and the selected tab is collapsed. There's no trickery you can do to avoid this.
My implementation helps in this scenario, introducing a new event to handle ItemChanges separately. I share in the hope it helps.