using System.ComponentModel; using System.Collections.Specialized; // uck should we be doing this namespace System.Collections.ObjectModel; /// /// A dictionary with callbacks for when changes occur. /// public class ObservableDictionary : IDictionary, INotifyCollectionChanged, INotifyPropertyChanged { private const string CountString = "Count"; private const string IndexerName = "Item[]"; private const string KeysName = "Keys"; private const string ValuesName = "Values"; private IDictionary _Dictionary; /// /// The dictionary being observed. /// protected IDictionary Dictionary { get { return _Dictionary; } } #region Constructors public ObservableDictionary() { _Dictionary = new Dictionary(); } public ObservableDictionary( IDictionary dictionary ) { _Dictionary = new Dictionary( dictionary ); } public ObservableDictionary( IEqualityComparer comparer ) { _Dictionary = new Dictionary( comparer ); } public ObservableDictionary( int capacity ) { _Dictionary = new Dictionary( capacity ); } public ObservableDictionary( IDictionary dictionary, IEqualityComparer comparer ) { _Dictionary = new Dictionary( dictionary, comparer ); } public ObservableDictionary( int capacity, IEqualityComparer comparer ) { _Dictionary = new Dictionary( capacity, comparer ); } #endregion #region IDictionary Members public void Add( TKey key, TValue value ) { Insert( key, value, true ); } public bool ContainsKey( TKey key ) { return Dictionary.ContainsKey( key ); } public ICollection Keys { get { return Dictionary.Keys; } } public bool Remove( TKey key ) { if ( key == null ) throw new ArgumentNullException( "key" ); TValue value; Dictionary.TryGetValue( key, out value ); var removed = Dictionary.Remove( key ); if ( removed ) //OnCollectionChanged(NotifyCollectionChangedAction.Remove, new KeyValuePair(key, value)); OnCollectionChanged(); return removed; } public bool TryGetValue( TKey key, out TValue value ) { return Dictionary.TryGetValue( key, out value ); } public ICollection Values { get { return Dictionary.Values; } } public TValue this[TKey key] { get { return Dictionary[key]; } set { Insert( key, value, false ); } } #endregion #region ICollection> Members public void Add( KeyValuePair item ) { Insert( item.Key, item.Value, true ); } public void Clear() { if ( Dictionary.Count > 0 ) { Dictionary.Clear(); OnCollectionChanged(); } } public bool Contains( KeyValuePair item ) { return Dictionary.Contains( item ); } public void CopyTo( KeyValuePair[] array, int arrayIndex ) { Dictionary.CopyTo( array, arrayIndex ); } public int Count { get { return Dictionary.Count; } } public bool IsReadOnly { get { return Dictionary.IsReadOnly; } } public bool Remove( KeyValuePair item ) { return Remove( item.Key ); } #endregion #region IEnumerable> Members public IEnumerator> GetEnumerator() { return Dictionary.GetEnumerator(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)Dictionary).GetEnumerator(); } #endregion #region INotifyCollectionChanged Members /// /// Called when the dictionary's key-value pairs have changed. /// public event NotifyCollectionChangedEventHandler CollectionChanged; #endregion #region INotifyPropertyChanged Members /// public event PropertyChangedEventHandler PropertyChanged; #endregion /// /// Merge given dictionary into this one. /// /// The items to add into this dictionary. /// Thrown when the input dictionary is null. /// Thrown when this dictionary already has an element with same key as the input dictionary. public void AddRange( IDictionary items ) { if ( items == null ) throw new ArgumentNullException( "items" ); if ( items.Count > 0 ) { if ( Dictionary.Count > 0 ) { if ( items.Keys.Any( ( k ) => Dictionary.ContainsKey( k ) ) ) throw new ArgumentException( "An item with the same key has already been added." ); else foreach ( var item in items ) Dictionary.Add( item ); } else _Dictionary = new Dictionary( items ); OnCollectionChanged( NotifyCollectionChangedAction.Add, items.ToArray() ); } } private void Insert( TKey key, TValue value, bool add ) { if ( key == null ) throw new ArgumentNullException( "key" ); TValue item; if ( Dictionary.TryGetValue( key, out item ) ) { if ( add ) throw new ArgumentException( "An item with the same key has already been added." ); if ( Equals( item, value ) ) return; Dictionary[key] = value; OnCollectionChanged( NotifyCollectionChangedAction.Replace, new KeyValuePair( key, value ), new KeyValuePair( key, item ) ); } else { Dictionary[key] = value; OnCollectionChanged( NotifyCollectionChangedAction.Add, new KeyValuePair( key, value ) ); } } private void OnPropertyChanged() { OnPropertyChanged( CountString ); OnPropertyChanged( IndexerName ); OnPropertyChanged( KeysName ); OnPropertyChanged( ValuesName ); } /// /// Called when a property (such as element count) of the dictionary has changed. /// protected virtual void OnPropertyChanged( string propertyName ) { if ( PropertyChanged != null ) PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) ); } private void OnCollectionChanged() { OnPropertyChanged(); if ( CollectionChanged != null ) CollectionChanged( this, new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Reset ) ); } private void OnCollectionChanged( NotifyCollectionChangedAction action, KeyValuePair changedItem ) { OnPropertyChanged(); if ( CollectionChanged != null ) CollectionChanged( this, new NotifyCollectionChangedEventArgs( action, changedItem ) ); } private void OnCollectionChanged( NotifyCollectionChangedAction action, KeyValuePair newItem, KeyValuePair oldItem ) { OnPropertyChanged(); if ( CollectionChanged != null ) CollectionChanged( this, new NotifyCollectionChangedEventArgs( action, newItem, oldItem ) ); } private void OnCollectionChanged( NotifyCollectionChangedAction action, IList newItems ) { OnPropertyChanged(); if ( CollectionChanged != null ) CollectionChanged( this, new NotifyCollectionChangedEventArgs( action, newItems ) ); } }