How to create a custom observable collection using ConcurrentDictionary, INotifyCollectionChanged, INotifyPropertyChanged
I wasn't able to get the OPs sample to work. Raised a trunk load of cross threading exceptions all the time no matter what I tried.
However, since I was also in the need of a thread safe collection that implements the INotifyCollectionChanged and INotifyPropertyChanged interfaces, I googled around and found an implementation from the guys at Microsoft themselves.
Download this file http://code.msdn.microsoft.com/Samples-for-Parallel-b4b76364 and search for ObservableConcurrentDictionary.cs in the archive.
Works like a charm!
Quickly going through your code without any eplanation from your side I can only guess.
I dont think setting Action on NotifyCollectionChangedEventArgs is enough. There are also NewItems
, OldItems
properties, that tell subscriber what items got changed.
Also note that, while those are collections, many WPF components support only single item change at a time through DataBinding.
I developed a tight version of an 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;
}
}
}