Question

I am trying to create an ObservableConcurrentDictionary. This object will be used in a multithreaded application, and it's data is used to populate a control via the controls ItemsSource property.

This is the implementation i have come up with:

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 as intended - in fact, it doesn't work at all. Any ideas on what i am doing wrong or do any better solutions exist?

Please note i CAN'T USE ObservableCollection, hence i have to write my own Observable collection.

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

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;
}
Was it helpful?

Solution

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.

OTHER TIPS

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!

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;
        }

    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top