// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.md file in the project root for full license information. // using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Threading; using System.Diagnostics; namespace System.Collections.Concurrent { /// /// Provides a thread-safe dictionary for use with data binding. /// /// Specifies the type of the keys in this collection. /// Specifies the type of the values in this collection. [DebuggerDisplay("Count={Count}")] public class ObservableConcurrentDictionary : ICollection>, IDictionary, INotifyCollectionChanged, INotifyPropertyChanged { private readonly SynchronizationContext _context; private readonly ConcurrentDictionary _dictionary; /// /// Initializes an instance of the ObservableConcurrentDictionary class. /// public ObservableConcurrentDictionary() { _context = AsyncOperationManager.SynchronizationContext; _dictionary = new ConcurrentDictionary(); } /// Event raised when the collection changes. public event NotifyCollectionChangedEventHandler CollectionChanged; /// Event raised when a property on the collection changes. public event PropertyChangedEventHandler PropertyChanged; /// /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary. /// private void NotifyObserversOfChange() { var collectionHandler = CollectionChanged; var propertyHandler = PropertyChanged; _context.Post(s => { collectionHandler?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); propertyHandler?.Invoke(this, new PropertyChangedEventArgs("Count")); propertyHandler?.Invoke(this, new PropertyChangedEventArgs(nameof(Keys))); propertyHandler?.Invoke(this, new PropertyChangedEventArgs(nameof(Values))); }, null); } /// Attempts to add an item to the dictionary, notifying observers of any changes. /// The item to be added. /// Whether the add was successful. private bool TryAddWithNotification(KeyValuePair item) => TryAddWithNotification(item.Key, item.Value); /// Attempts to add an item to the dictionary, notifying observers of any changes. /// The key of the item to be added. /// The value of the item to be added. /// Whether the add was successful. private bool TryAddWithNotification(TKey key, TValue value) { bool result = _dictionary.TryAdd(key, value); if (result) NotifyObserversOfChange(); return result; } /// Attempts to remove an item from the dictionary, notifying observers of any changes. /// The key of the item to be removed. /// The value of the item removed. /// Whether the removal was successful. private bool TryRemoveWithNotification(TKey key, out TValue value) { bool result = _dictionary.TryRemove(key, out value); if (result) NotifyObserversOfChange(); return result; } /// Attempts to add or update an item in the dictionary, notifying observers of any changes. /// The key of the item to be updated. /// The new value to set for the item. /// Whether the update was successful. private void UpdateWithNotification(TKey key, TValue value) { _dictionary[key] = value; NotifyObserversOfChange(); } #region ICollection> Members void ICollection>.Add(KeyValuePair item) => TryAddWithNotification(item); void ICollection>.Clear() { ((ICollection>)_dictionary).Clear(); NotifyObserversOfChange(); } bool ICollection>.Contains(KeyValuePair item) => ((ICollection>)_dictionary).Contains(item); void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) => ((ICollection>)_dictionary).CopyTo(array, arrayIndex); int ICollection>.Count => ((ICollection>)_dictionary).Count; bool ICollection>.IsReadOnly => ((ICollection>)_dictionary).IsReadOnly; bool ICollection>.Remove(KeyValuePair item) => TryRemoveWithNotification(item.Key, out _); #endregion #region IEnumerable> Members IEnumerator> IEnumerable>.GetEnumerator() => ((ICollection>)_dictionary).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((ICollection>)_dictionary).GetEnumerator(); #endregion #region IDictionary Members public void Add(TKey key, TValue value) => TryAddWithNotification(key, value); public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key); public ICollection Keys => _dictionary.Keys; public bool Remove(TKey key) => TryRemoveWithNotification(key, out _); public bool TryGetValue(TKey key, out TValue value) => _dictionary.TryGetValue(key, out value); public ICollection Values => _dictionary.Values; public TValue this[TKey key] { get => _dictionary[key]; set => UpdateWithNotification(key, value); } #endregion } }