/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// /// From https://gist.github.com/kzu/cfe3cb6e4fe3efea6d24 using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Dynamic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wasSharp.Collections.Specialized { /// /// Provides a 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 ObservableDictionary : ICollection>, IDictionary, INotifyCollectionChanged, INotifyPropertyChanged { readonly IDictionary dictionary; /// Event raised when the collection changes. public event NotifyCollectionChangedEventHandler CollectionChanged = (sender, args) => { }; /// Event raised when a property on the collection changes. public event PropertyChangedEventHandler PropertyChanged = (sender, args) => { }; /// /// Initializes an instance of the class. /// public ObservableDictionary() : this(new Dictionary()) { } /// /// Initializes an instance of the class using another dictionary as /// the key/value store. /// public ObservableDictionary(IDictionary dictionary) { this.dictionary = dictionary; } void AddWithNotification(KeyValuePair item) { AddWithNotification(item.Key, item.Value); } void AddWithNotification(TKey key, TValue value) { dictionary.Add(key, value); CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair(key, value))); PropertyChanged(this, new PropertyChangedEventArgs("Count")); PropertyChanged(this, new PropertyChangedEventArgs("Keys")); PropertyChanged(this, new PropertyChangedEventArgs("Values")); } bool RemoveWithNotification(TKey key) { TValue value; if (dictionary.TryGetValue(key, out value) && dictionary.Remove(key)) { CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new KeyValuePair(key, value))); PropertyChanged(this, new PropertyChangedEventArgs("Count")); PropertyChanged(this, new PropertyChangedEventArgs("Keys")); PropertyChanged(this, new PropertyChangedEventArgs("Values")); return true; } return false; } public void Clear() { dictionary.Clear(); CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); PropertyChanged(this, new PropertyChangedEventArgs("Count")); PropertyChanged(this, new PropertyChangedEventArgs("Keys")); PropertyChanged(this, new PropertyChangedEventArgs("Values")); } void UpdateWithNotification(TKey key, TValue value) { TValue existing; if (dictionary.TryGetValue(key, out existing)) { dictionary[key] = value; CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, new KeyValuePair(key, value), new KeyValuePair(key, existing))); PropertyChanged(this, new PropertyChangedEventArgs("Values")); } else { AddWithNotification(key, value); } } /// /// Allows derived classes to raise custom property changed events. /// protected void RaisePropertyChanged(PropertyChangedEventArgs args) { PropertyChanged(this, args); } #region IDictionary Members /// /// Adds an element with the provided key and value to the . /// /// The object to use as the key of the element to add. /// The object to use as the value of the element to add. public void Add(TKey key, TValue value) { AddWithNotification(key, value); } /// /// Determines whether the contains an element with the specified key. /// /// The key to locate in the . /// /// true if the contains an element with the key; otherwise, false. /// public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); } /// /// Gets an containing the keys of the . /// /// An containing the keys of the object that implements . public ICollection Keys { get { return dictionary.Keys; } } /// /// Removes the element with the specified key from the . /// /// The key of the element to remove. /// /// true if the element is successfully removed; otherwise, false. This method also returns false if was not found in the original . /// public bool Remove(TKey key) { return RemoveWithNotification(key); } /// /// Gets the value associated with the specified key. /// /// The key whose value to get. /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. /// /// true if the object that implements contains an element with the specified key; otherwise, false. /// public bool TryGetValue(TKey key, out TValue value) { return dictionary.TryGetValue(key, out value); } /// /// Gets an containing the values in the . /// /// An containing the values in the object that implements . public ICollection Values { get { return dictionary.Values; } } /// /// Gets or sets the element with the specified key. /// /// The key. /// public TValue this[TKey key] { get { return dictionary[key]; } set { UpdateWithNotification(key, value); } } #endregion #region ICollection> Members void ICollection>.Add(KeyValuePair item) { AddWithNotification(item); } void ICollection>.Clear() { dictionary.Clear(); CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); PropertyChanged(this, new PropertyChangedEventArgs("Count")); PropertyChanged(this, new PropertyChangedEventArgs("Keys")); PropertyChanged(this, new PropertyChangedEventArgs("Values")); } bool ICollection>.Contains(KeyValuePair item) => dictionary.Contains(item); void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { dictionary.CopyTo(array, arrayIndex); } int ICollection>.Count => dictionary.Count(); bool ICollection>.IsReadOnly => dictionary.IsReadOnly; bool ICollection>.Remove(KeyValuePair item) => RemoveWithNotification(item.Key); #endregion #region IEnumerable> Members IEnumerator> IEnumerable>.GetEnumerator() => dictionary.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => dictionary.GetEnumerator(); #endregion #region Extensions public IEnumerator> GetEnumerator() { return dictionary.GetEnumerator(); } #endregion } }