How to create a custom observable collection using ConcurrentDictionary, INotifyCollectionChanged, INotifyPropertyChanged

I am trying to create an ObservableConcurrentDictionary. This object will be used in a multi-threaded application, and its data is used to populate the control through the ItemsSource property of the controls.

This is the implementation I came across:

public sealed class ObservableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged { #region Constructors public ObservableConcurrentDictionary() : base() { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : base(collection) { } public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity) : base(concurrencyLevel, capacity) { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(collection, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, capacity, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, collection, comparer) { } #endregion #region Public Methods public new TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) { // Update value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace)); } // Else if key does not exist else { // Add value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); } // Returns the value return value; } public void Clear() { // Clear dictionary base.Clear(); // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) // Get value value = base.GetOrAdd(key, valueFactory); // Else if key does not exist else { // Add value and raise event value = base.GetOrAdd(key, valueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); } // Return value return value; } public new TValue GetOrAdd(TKey key, TValue value) { // If key exists if (base.ContainsKey(key)) // Get value base.GetOrAdd(key, value); // Else if key does not exist else { // Add value and raise event base.GetOrAdd(key, value); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); } // Return value return value; } public new bool TryAdd(TKey key, TValue value) { // Stores tryAdd bool tryAdd; // If added if (tryAdd = base.TryAdd(key, value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); // Return tryAdd return tryAdd; } public new bool TryRemove(TKey key, out TValue value) { // Stores tryRemove bool tryRemove; // If removed if (tryRemove = base.TryRemove(key, out value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove)); // Return tryAdd return tryRemove; } public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { // Stores tryUpdate bool tryUpdate; // If updated if (tryUpdate = base.TryUpdate(key, newValue, comparisonValue)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace)); // Return tryUpdate return tryUpdate; } #endregion #region Private Methods private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (CollectionChanged != null) CollectionChanged(this, e); } #endregion #region INotifyCollectionChanged Members public event NotifyCollectionChangedEventHandler CollectionChanged; #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion } 

Unfortunately, the solution does not work properly - in fact, it does not work at all. Any ideas on what I am doing wrong or are there any better solutions?

Note that I CANNOT USE an ObservableCollection, so I need to write my own Observable collection.

EDIT: The working version is below. Hope this helps someone else with a similar issue.

 public sealed class ObservableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged { public ObservableConcurrentDictionary() : base() { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : base(collection) { } public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity) : base(concurrencyLevel, capacity) { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(collection, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, capacity, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, collection, comparer) { } public new TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) { // Update value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value)); } // Else if key does not exist else { // Add value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); } // Returns the value return value; } public new void Clear() { // Clear dictionary base.Clear(); // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) // Get value value = base.GetOrAdd(key, valueFactory); // Else if key does not exist else { // Add value and raise event value = base.GetOrAdd(key, valueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); } // Return value return value; } public new TValue GetOrAdd(TKey key, TValue value) { // If key exists if (base.ContainsKey(key)) // Get value base.GetOrAdd(key, value); // Else if key does not exist else { // Add value and raise event base.GetOrAdd(key, value); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); } // Return value return value; } public new bool TryAdd(TKey key, TValue value) { // Stores tryAdd bool tryAdd; // If added if (tryAdd = base.TryAdd(key, value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); // Return tryAdd return tryAdd; } public new bool TryRemove(TKey key, out TValue value) { // Stores tryRemove bool tryRemove; // If removed if (tryRemove = base.TryRemove(key, out value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value)); // Return tryAdd return tryRemove; } public new bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { // Stores tryUpdate bool tryUpdate; // If updated if (tryUpdate = base.TryUpdate(key, newValue, comparisonValue)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue)); // Return tryUpdate return tryUpdate; } private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (CollectionChanged != null) CollectionChanged(this, e); } public event NotifyCollectionChangedEventHandler CollectionChanged; public event PropertyChangedEventHandler PropertyChanged; } 
+7
wpf inotifypropertychanged inotifycollectionchanged
source share
3 answers

Quickly looking through your code without any kind of inflation on your part, I can only guess. I don't think setting Action for NotifyCollectionChangedEventArgs is enough. There are also NewItems , OldItems properties that tell the subscriber which items have changed.

Also note that although these are collections, many WPF components support only one element change at a time through a DataBinding.

+2
source share

I failed to get the sample OPs to work. Raised the luggage load to cross threaded exceptions all the time no matter what I tried.

However, since I also needed a streaming security assembly that implements the INotifyCollectionChanged and INotifyPropertyChanged interfaces, I googled around and found an implementation from guys from Microsoft itself.

Download this file http://code.msdn.microsoft.com/Samples-for-Parallel-b4b76364 and find the ObservableConcurrentDictionary.cs in the archive.

It works like a charm!

+11
source share

I developed a dense version of ObservableConcurrentDictionnary, please comment / suggest ...

... where TValue: Object {use your class instead of Object ...}

Qurlet

 using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; namespace Collections { public class ObservableConcurrentDictionary<TValue> : ConcurrentDictionary<Int32, TValue>, INotifyCollectionChanged, INotifyPropertyChanged where TValue : Object , new() { public event NotifyCollectionChangedEventHandler CollectionChanged; protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs changeAction) { var eh = CollectionChanged; if (eh == null) return; eh(this, changeAction); OnPropertyChanged(); } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged() { var eh = PropertyChanged; if (eh == null) return; // All properties : Keys, Values, Count, IsEmpty eh(this, new PropertyChangedEventArgs(null)); } #region Ctors public ObservableConcurrentDictionary() : base() { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<Int32, TValue>> collection) : base(collection) { } public ObservableConcurrentDictionary(IEqualityComparer<Int32> comparer) : base(comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity) : base(concurrencyLevel, capacity) { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<Int32, TValue>> collection, IEqualityComparer<Int32> comparer) : base(collection, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<Int32> comparer) : base(concurrencyLevel, capacity, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<Int32, TValue>> collection, IEqualityComparer<Int32> comparer) : base(concurrencyLevel, collection, comparer) { } #endregion public new void Clear() { // Clear dictionary base.Clear(); // Raise event OnCollectionChanged(changeAction: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public new TValue AddOrUpdate(Int32 key, Func<Int32, TValue> addValueFactory, Func<Int32, TValue, TValue> updateValueFactory) { bool isUpdated = false; TValue oldValue = default(TValue); TValue value = base.AddOrUpdate(key, addValueFactory, (k, v) => { isUpdated = true; oldValue = v; return updateValueFactory(k, v); }); if (isUpdated) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return value; } public new TValue AddOrUpdate(Int32 key, TValue addValue, Func<Int32, TValue, TValue> updateValueFactory) { bool isUpdated = false; TValue oldValue = default(TValue); TValue value = base.AddOrUpdate(key, addValue, (k, v) => { isUpdated = true; oldValue = v; return updateValueFactory(k, v); }); if (isUpdated) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return value; } public new TValue GetOrAdd(Int32 key, Func<Int32, TValue> addValueFactory) { bool isAdded = false; TValue value = base.GetOrAdd(key, k => { isAdded = true; return addValueFactory(k); }); if (isAdded) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return value; } public new TValue GetOrAdd(Int32 key, TValue value) { return GetOrAdd(key, k => value); } public new bool TryAdd(Int32 key, TValue value) { bool tryAdd = base.TryAdd(key, value); if (tryAdd) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return tryAdd; } public new bool TryRemove(Int32 key, out TValue value) { // Stores tryRemove bool tryRemove = base.TryRemove(key, out value); // If removed raise event if (tryRemove) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value)); return tryRemove; } public new bool TryUpdate(Int32 key, TValue newValue, TValue comparisonValue) { // Stores tryUpdate bool tryUpdate = base.TryUpdate(key, newValue, comparisonValue); if (tryUpdate) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, comparisonValue)); return tryUpdate; } } } 
+1
source share

All Articles