Как создать настраиваемую наблюдаемую коллекцию, используя ConcurrentDictionary, INotifyCollectionChanged, INotifyPropertyChanged - PullRequest
8 голосов
/ 14 октября 2010

Я пытаюсь создать ObservableConcurrentDictionary.Этот объект будет использоваться в многопоточном приложении, а его данные используются для заполнения элемента управления через свойство ItemsSource элементов управления.

Это реализация, которую я придумал:

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
}

К сожалению, решение работает не так, как задумано - фактически оно не работает вообще.Любые идеи о том, что я делаю не так или существуют какие-то лучшие решения?

Обратите внимание, я не могу использовать ObservableCollection, поэтому я должен написать свою собственную коллекцию Observable.: Рабочая версия ниже.Надеюсь, это поможет кому-то еще с подобной проблемой.

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

Ответы [ 3 ]

11 голосов
/ 08 февраля 2013

Я не смог заставить работать образец OP. Постоянно увеличивал нагрузку на магистраль из-за перекрестных потоков независимо от того, что я пытался.

Однако, поскольку мне также требовалась многопоточная безопасная коллекция, которая реализует интерфейсы INotifyCollectionChanged и INotifyPropertyChanged, я нашел гугл и нашел реализацию от ребят из Microsoft.

Загрузите этот файл http://code.msdn.microsoft.com/Samples-for-Parallel-b4b76364 и найдите ObservableConcurrentDictionary.cs в архиве.

Работает как шарм!

2 голосов
/ 27 августа 2013

Я разработал ограниченную версию ObservableConcurrentDictionnary, пожалуйста, комментируйте / предлагайте ...

... где TValue: Object {используйте ваш класс вместо Object ...}

Курлет

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

    }
}
2 голосов
/ 14 октября 2010

Быстро просматривая ваш код без каких-либо объяснений с вашей стороны, я могу только догадываться.Я не думаю, что установки Action на NotifyCollectionChangedEventArgs достаточно.Существуют также свойства NewItems, OldItems, которые сообщают подписчику, какие элементы были изменены.

Также обратите внимание, что, хотя они являются коллекциями, многие компоненты WPF поддерживают только изменение одного элемента за один раз посредством DataBinding.

...