Подтвердить что ты не робот

.NET ObservableDictionary

Я написал следующий класс, который реализует (или пытается!) словарь с уведомлениями:

public partial class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, INotifyCollectionChanged
{
    public ObservableDictionary() : base() { }
    public ObservableDictionary(int capacity) : base(capacity) { }
    public ObservableDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { }
    public ObservableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary) { }
    public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer) { }
    public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer) { }

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public new TValue this[TKey key]
    {
        get
        {
            return base[key];
        }
        set
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, key, 0));
            base[key] = value;
        }
    }

    public new void Add(TKey key, TValue value)
    {
        base.Add(key, value);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, key, 0));
    }

    public new bool Remove(TKey key)
    {
        bool x = base.Remove(key);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, key, 0));
        return x;
    }

    public new void Clear()
    {
        base.Clear();
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }


    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
        {
            CollectionChanged(this, e);
        }
    }
}

В другом классе у меня есть слушатель для события MyObservableDictionary.CollectionChanged:

Проблема, с которой я сталкиваюсь, заключается в том, что событие не срабатывает. Как я могу это исправить?

4b9b3361

Ответ 1

Я предлагаю вам реализовать IDictionary<TKey, TValue> вместо наследования с Dictionary<TKey, TValue>. Поскольку вы должны использовать new, а не override, возможно, что методы просто вызываются в базовом классе, а не в вашем классе. У меня возникло бы желание использовать Dictionary<TKey, TValue> для внутреннего хранения данных.

Фактически я нашел это:  http://blogs.microsoft.co.il/blogs/shimmy/archive/2010/12/26/observabledictionary-lt-tkey-tvalue-gt-c.aspx

Ответ 2

Microsoft ParallelExtensionsExtras предоставляет этот класс, который не только можно наблюдать, но и одновременно:

Теперь доступно через Nuget: https://www.nuget.org/packages/MSFT.ParallelExtensionsExtras/

Обратите внимание, что некоторые функции теперь являются частью более новых платформ .NET. ParallelExtensions "Extras" все еще имеют значение?

Блог Microsoft Tour: https://blogs.msdn.microsoft.com/pfxteam/2010/04/04/a-tour-of-parallelextensionsextras/

Пример кода параллельного программирования (исходный код параллельных расширений): https://code.msdn.microsoft.com/ParExtSamples

//--------------------------------------------------------------------------
// 
//  Copyright (c) Microsoft Corporation.  All rights reserved. 
// 
//  File: ObservableConcurrentDictionary.cs
//
//--------------------------------------------------------------------------

using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;

namespace System.Collections.Concurrent
{
    /// <summary>
    /// Provides a thread-safe dictionary for use with data binding.
    /// </summary>
    /// <typeparam name="TKey">Specifies the type of the keys in this collection.</typeparam>
    /// <typeparam name="TValue">Specifies the type of the values in this collection.</typeparam>
    [DebuggerDisplay("Count={Count}")]
    public class ObservableConcurrentDictionary<TKey, TValue> :
        ICollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>,
        INotifyCollectionChanged, INotifyPropertyChanged
    {
        private readonly SynchronizationContext _context;
        private readonly ConcurrentDictionary<TKey, TValue> _dictionary;

        /// <summary>
        /// Initializes an instance of the ObservableConcurrentDictionary class.
        /// </summary>
        public ObservableConcurrentDictionary()
        {
            _context = AsyncOperationManager.SynchronizationContext;
            _dictionary = new ConcurrentDictionary<TKey, TValue>();
        }

        /// <summary>Event raised when the collection changes.</summary>
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        /// <summary>Event raised when a property on the collection changes.</summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
        /// </summary>
        private void NotifyObserversOfChange()
        {
            var collectionHandler = CollectionChanged;
            var propertyHandler = PropertyChanged;
            if (collectionHandler != null || propertyHandler != null)
            {
                _context.Post(s =>
                {
                    if (collectionHandler != null)
                    {
                        collectionHandler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                    }
                    if (propertyHandler != null)
                    {
                        propertyHandler(this, new PropertyChangedEventArgs("Count"));
                        propertyHandler(this, new PropertyChangedEventArgs("Keys"));
                        propertyHandler(this, new PropertyChangedEventArgs("Values"));
                    }
                }, null);
            }
        }

        /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
        /// <param name="item">The item to be added.</param>
        /// <returns>Whether the add was successful.</returns>
        private bool TryAddWithNotification(KeyValuePair<TKey, TValue> item)
        {
            return TryAddWithNotification(item.Key, item.Value);
        }

        /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
        /// <param name="key">The key of the item to be added.</param>
        /// <param name="value">The value of the item to be added.</param>
        /// <returns>Whether the add was successful.</returns>
        private bool TryAddWithNotification(TKey key, TValue value)
        {
            bool result = _dictionary.TryAdd(key, value);
            if (result) NotifyObserversOfChange();
            return result;
        }

        /// <summary>Attempts to remove an item from the dictionary, notifying observers of any changes.</summary>
        /// <param name="key">The key of the item to be removed.</param>
        /// <param name="value">The value of the item removed.</param>
        /// <returns>Whether the removal was successful.</returns>
        private bool TryRemoveWithNotification(TKey key, out TValue value)
        {
            bool result = _dictionary.TryRemove(key, out value);
            if (result) NotifyObserversOfChange();
            return result;
        }

        /// <summary>Attempts to add or update an item in the dictionary, notifying observers of any changes.</summary>
        /// <param name="key">The key of the item to be updated.</param>
        /// <param name="value">The new value to set for the item.</param>
        /// <returns>Whether the update was successful.</returns>
        private void UpdateWithNotification(TKey key, TValue value)
        {
            _dictionary[key] = value;
            NotifyObserversOfChange();
        }

        #region ICollection<KeyValuePair<TKey,TValue>> Members
        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            TryAddWithNotification(item);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
        {
            ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Clear();
            NotifyObserversOfChange();
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
        {
            return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Contains(item);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);
        }

        int ICollection<KeyValuePair<TKey, TValue>>.Count
        {
            get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Count; }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
        {
            get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly; }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            TValue temp;
            return TryRemoveWithNotification(item.Key, out temp);
        }
        #endregion

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members
        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
        {
            return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
        }
        #endregion

        #region IDictionary<TKey,TValue> Members
        public void Add(TKey key, TValue value)
        {
            TryAddWithNotification(key, value);
        }

        public bool ContainsKey(TKey key)
        {
            return _dictionary.ContainsKey(key);
        }

        public ICollection<TKey> Keys
        {
            get { return _dictionary.Keys; }
        }

        public bool Remove(TKey key)
        {
            TValue temp;
            return TryRemoveWithNotification(key, out temp);
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return _dictionary.TryGetValue(key, out value);
        }

        public ICollection<TValue> Values
        {
            get { return _dictionary.Values; }
        }

        public TValue this[TKey key]
        {
            get { return _dictionary[key]; }
            set { UpdateWithNotification(key, value); }
        }
        #endregion
    }
}

Ответ 3

Ваше решение - Исправлено;)

public class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged {
    public ObservableDictionary( ) : base( ) { }
    public ObservableDictionary(int capacity) : base(capacity) { }
    public ObservableDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { }
    public ObservableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary) { }
    public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer) { }
    public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer) { }

    public event NotifyCollectionChangedEventHandler CollectionChanged;
    public event PropertyChangedEventHandler PropertyChanged;

    public new TValue this[TKey key] {
        get {
            return base[key];
        }
        set {
            TValue oldValue;
            bool exist = base.TryGetValue(key, out oldValue);
            var oldItem = new KeyValuePair<TKey, TValue>(key, oldValue);
            base[key] = value;
            var newItem = new KeyValuePair<TKey, TValue>(key, value);
            if (exist) {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, base.Keys.ToList( ).IndexOf(key)));
            } else {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItem, base.Keys.ToList( ).IndexOf(key)));
                this.OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
            }
        }
    }

    public new void Add(TKey key, TValue value) {
        if (!base.ContainsKey(key)) {
            var item = new KeyValuePair<TKey, TValue>(key, value);
            base.Add(key, value);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, base.Keys.ToList( ).IndexOf(key)));
            this.OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
        }
    }

    public new bool Remove(TKey key) {
        TValue value;
        if (base.TryGetValue(key, out value)) {
            var item = new KeyValuePair<TKey, TValue>(key, base[key]);
            bool result = base.Remove(key);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, base.Keys.ToList( ).IndexOf(key)));
            this.OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
            return result;
        }
        return false;
    }

    public new void Clear( ) {
        base.Clear( );
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        this.OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
    }

    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) {
        if (this.CollectionChanged != null) {
            this.CollectionChanged(this, e);
        }
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
        if (this.PropertyChanged != null) {
            this.PropertyChanged(this, e);
        }
    }
}

Ответ 4

Я выкатил свой собственный: https://www.nuget.org/packages/hellosam.net.collections/

Использует дерево AVL, поэтому операции O (log N) вместо этого, где большинство реализаций, которые я видел с помощью List.indexOf(), являются O (N).

Он может даже наблюдать за вашим элементом INotifyPropertyChanged и преобразовывать их в наблюдаемое событие коллекции, чтобы сохранить ответ сортировки/группы DataGrid на изменение.

Ответ 5

Как указали в этом ответе Игнатио и Мэтью, только повышение уведомления об изменении коллекции Reset является некорректным и не очень полезным, если вызывающий должен знать, что на самом деле изменилось. К счастью, это легко исправить. Обратите внимание, что эта версия использует Send вместо Post как Натан упоминал в предыдущем ответе, потому что WPF чувствителен к тому, чтобы сообщать правильный индекс при удалении, и неправильное его получение приводит к этому запутанному исключению. (Будьте бдительны: я все еще не совсем убежден, что указанный индекс будет полностью надежным, если будет много перекрывающихся изменений, особенно учитывая, что словари должны рассматриваться как неупорядоченные.)

//--------------------------------------------------------------------------
// 
//  Copyright (c) Microsoft Corporation.  All rights reserved. 
// 
//  File: ObservableConcurrentDictionary.cs
//
//--------------------------------------------------------------------------

using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;

namespace System.Collections.Concurrent
{
    /// <summary>
    /// Provides a thread-safe dictionary for use with data binding.
    /// </summary>
    /// <typeparam name="TKey">Specifies the type of the keys in this collection.</typeparam>
    /// <typeparam name="TValue">Specifies the type of the values in this collection.</typeparam>
    [DebuggerDisplay("Count={Count}")]
    public class ObservableConcurrentDictionary<TKey, TValue> :
        ICollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>,
        INotifyCollectionChanged, INotifyPropertyChanged
    {
        private readonly SynchronizationContext _context;
        private readonly ConcurrentDictionary<TKey, TValue> _dictionary;

        /// <summary>
        /// Initializes an instance of the ObservableConcurrentDictionary class.
        /// </summary>
        public ObservableConcurrentDictionary()
        {
            _context = AsyncOperationManager.SynchronizationContext;
            _dictionary = new ConcurrentDictionary<TKey, TValue>();
        }

        /// <summary>Event raised when the collection changes.</summary>
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        /// <summary>Event raised when a property on the collection changes.</summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
        /// </summary>
        private void NotifyObserversOfChange()
        {
            var collectionHandler = CollectionChanged;
            var propertyHandler = PropertyChanged;
            if (collectionHandler != null || propertyHandler != null)
            {
                _context.Send(s =>
                {
                    collectionHandler?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Count"));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Keys"));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Values"));
                }, null);
            }
        }

        /// <summary>
        /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
        /// </summary>
        /// <param name="actionType">Add or Update action</param>
        /// <param name="changedItem">The item involved with the change</param>
        private void NotifyObserversOfChange(NotifyCollectionChangedAction actionType, object changedItem)
        {
            var collectionHandler = CollectionChanged;
            var propertyHandler = PropertyChanged;
            if (collectionHandler != null || propertyHandler != null)
            {
                _context.Send(s =>
                {
                    collectionHandler?.Invoke(this, new NotifyCollectionChangedEventArgs(actionType, changedItem));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Count"));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Keys"));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Values"));
                }, null);
            }
        }

        /// <summary>
        /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
        /// </summary>
        /// <param name="actionType">Remove action or optionally an Add action</param>
        /// <param name="item">The item in question</param>
        /// <param name="index">The position of the item in the collection</param>
        private void NotifyObserversOfChange(NotifyCollectionChangedAction actionType, object item, int index)
        {
            var collectionHandler = CollectionChanged;
            var propertyHandler = PropertyChanged;
            if (collectionHandler != null || propertyHandler != null)
            {
                _context.Send(s =>
                {
                    collectionHandler?.Invoke(this, new NotifyCollectionChangedEventArgs(actionType, item, index));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Count"));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Keys"));
                    propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Values"));
                }, null);
            }
        }

        /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
        /// <param name="item">The item to be added.</param>
        /// <returns>Whether the add was successful.</returns>
        private bool TryAddWithNotification(KeyValuePair<TKey, TValue> item)
            => TryAddWithNotification(item.Key, item.Value);

        /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
        /// <param name="key">The key of the item to be added.</param>
        /// <param name="value">The value of the item to be added.</param>
        /// <returns>Whether the add was successful.</returns>
        private bool TryAddWithNotification(TKey key, TValue value)
        {
            bool result = _dictionary.TryAdd(key, value);
            int index = IndexOf(key);
            if (result) NotifyObserversOfChange(NotifyCollectionChangedAction.Add, value, index);
            return result;
        }

        /// <summary>Attempts to remove an item from the dictionary, notifying observers of any changes.</summary>
        /// <param name="key">The key of the item to be removed.</param>
        /// <param name="value">The value of the item removed.</param>
        /// <returns>Whether the removal was successful.</returns>
        private bool TryRemoveWithNotification(TKey key, out TValue value)
        {
            int index = IndexOf(key);
            bool result = _dictionary.TryRemove(key, out value);
            if (result) NotifyObserversOfChange(NotifyCollectionChangedAction.Remove, value, index);
            return result;
        }

        /// <summary>Attempts to add or update an item in the dictionary, notifying observers of any changes.</summary>
        /// <param name="key">The key of the item to be updated.</param>
        /// <param name="value">The new value to set for the item.</param>
        /// <returns>Whether the update was successful.</returns>
        private void UpdateWithNotification(TKey key, TValue value)
        {
            _dictionary[key] = value;
            NotifyObserversOfChange(NotifyCollectionChangedAction.Replace, value);
        }

        /// <summary>
        /// WPF requires that the reported index for Add/Remove events are correct/reliable. With a dictionary there
        /// is no choice but to brute-force search through the key list. Ugly.
        /// </summary>
        private int IndexOf(TKey key)
        {
            var keys = _dictionary.Keys;
            int index = -1;
            foreach(TKey k in keys)
            {
                index++;
                if (k.Equals(key)) return index;
            }
            return -1;
        }

        // ICollection<KeyValuePair<TKey,TValue>> Members


        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
            => TryAddWithNotification(item);

        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
        {
            _dictionary.Clear();
            NotifyObserversOfChange();
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
            => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Contains(item);

        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
            => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);

        int ICollection<KeyValuePair<TKey, TValue>>.Count
        {
            get => _dictionary.Count;
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
        {
            get => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly;
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
            => TryRemoveWithNotification(item.Key, out TValue temp);


        // IEnumerable<KeyValuePair<TKey,TValue>> Members


        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
            => _dictionary.GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator()
            => _dictionary.GetEnumerator();


        // IDictionary<TKey,TValue> Members


        public void Add(TKey key, TValue value)
            => TryAddWithNotification(key, value);

        public bool ContainsKey(TKey key)
            => _dictionary.ContainsKey(key);

        public ICollection<TKey> Keys
        {
            get { return _dictionary.Keys; }
        }

        public bool Remove(TKey key)
            => TryRemoveWithNotification(key, out TValue temp);

        public bool TryGetValue(TKey key, out TValue value)
            => _dictionary.TryGetValue(key, out value);

        public ICollection<TValue> Values
        {
            get => _dictionary.Values;
        }

        public TValue this[TKey key]
        {
            get => _dictionary[key];
            set => UpdateWithNotification(key, value);
        }
    }
}

Ответ 6

Как я могу это использовать? Есть ли пример?

Ответ 7

Мне удалось найти решение - обходное решение

public delegate void CollectionAlteredEventHander( object sender , EventArgs e);

public partial class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{

    /*Class contructors*/

    public event CollectionAlteredEventHander CollectionAltered;

    public new TValue this[TKey key]
    {
        get
        {
            return base[key];
        }
        set
        {
            OnCollectionAltered(new EventArgs());
            base[key] = value;
        }
    }

    public new void Add(TKey key, TValue value)
    {
        int idx = 0;
        if (!TryGetKeyIndex(this, key, ref idx))
        {
            base.Add(key, value);
            OnCollectionAltered(new EventArgs());
        }
    }

    public new bool Remove(TKey key)
    {
        int idx = 0; 
        if( TryGetKeyIndex( this ,key, ref idx))
        {
            OnCollectionAltered(new EventArgs());
            return base.Remove(key);
        }
        return false;
    }

    private bool TryGetKeyIndex(ObservableDictionary<TKey, TValue> observableDictionary, TKey key , ref int idx)
    {
        foreach (KeyValuePair<TKey, TValue> pair in observableDictionary) 
        {
            if (pair.Key.Equals(key)) 
            {
                return true;
            }
            idx++;
        }
        return false;
    }

    public new void Clear()
    {
        OnCollectionAltered(new EventArgs());
        base.Clear();            
    }

    protected virtual void OnCollectionAltered(EventArgs e)
    {
        if (CollectionAltered != null)
        {
            CollectionAltered(this, e);
        }
    }

}

Я больше не реализовал интерфейс INotifyCollectionChanged.