#region CPL License /* Nuclex Framework Copyright (C) 2002-2012 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections; using System.Collections.Generic; #if !NO_SPECIALIZED_COLLECTIONS using System.Collections.Specialized; #endif using System.Runtime.Serialization; namespace Nuclex.Support.Collections { /// A dictionary that sneds out change notifications /// Type of the keys used in the dictionary /// Type of the values used in the dictionary #if !NO_SERIALIZATION [Serializable] #endif public class ObservableDictionary : #if !NO_SERIALIZATION ISerializable, IDeserializationCallback, #endif IDictionary, IDictionary, #if !NO_SPECIALIZED_COLLECTIONS INotifyCollectionChanged, #endif IObservableCollection> { #if !NO_SERIALIZATION #region class SerializedDictionary /// /// Dictionary wrapped used to reconstruct a serialized read only dictionary /// private class SerializedDictionary : Dictionary { /// /// Initializes a new instance of the System.WeakReference class, using deserialized /// data from the specified serialization and stream objects. /// /// /// An object that holds all the data needed to serialize or deserialize the /// current System.WeakReference object. /// /// /// (Reserved) Describes the source and destination of the serialized stream /// specified by info. /// /// /// The info parameter is null. /// public SerializedDictionary(SerializationInfo info, StreamingContext context) : base(info, context) { } } #endregion // class SerializedDictionary #endif // !NO_SERIALIZATION /// Raised when an item has been added to the dictionary public event EventHandler>> ItemAdded; /// Raised when an item is removed from the dictionary public event EventHandler>> ItemRemoved; /// Raised when an item is replaced in the collection public event EventHandler>> ItemReplaced; /// Raised when the dictionary is about to be cleared public event EventHandler Clearing; /// Raised when the dictionary has been cleared public event EventHandler Cleared; #if !NO_SPECIALIZED_COLLECTIONS /// Called when the collection has changed public event NotifyCollectionChangedEventHandler CollectionChanged; #endif /// Initializes a new observable dictionary public ObservableDictionary() : this(new Dictionary()) { } /// Initializes a new observable Dictionary wrapper /// Dictionary that will be wrapped public ObservableDictionary(IDictionary dictionary) { this.typedDictionary = dictionary; this.objectDictionary = (this.typedDictionary as IDictionary); } #if !NO_SERIALIZATION /// /// Initializes a new instance of the System.WeakReference class, using deserialized /// data from the specified serialization and stream objects. /// /// /// An object that holds all the data needed to serialize or deserialize the /// current System.WeakReference object. /// /// /// (Reserved) Describes the source and destination of the serialized stream /// specified by info. /// /// /// The info parameter is null. /// protected ObservableDictionary(SerializationInfo info, StreamingContext context) : this(new SerializedDictionary(info, context)) { } #endif // !NO_SERIALIZATION /// Whether the directory is write-protected public bool IsReadOnly { get { return this.typedDictionary.IsReadOnly; } } /// /// Determines whether the specified KeyValuePair is contained in the Dictionary /// /// KeyValuePair that will be checked for /// True if the provided KeyValuePair was contained in the Dictionary public bool Contains(KeyValuePair item) { return this.typedDictionary.Contains(item); } /// Determines whether the Dictionary contains the specified key /// Key that will be checked for /// /// True if an entry with the specified key was contained in the Dictionary /// public bool ContainsKey(TKey key) { return this.typedDictionary.ContainsKey(key); } /// Copies the contents of the Dictionary into an array /// Array the Dictionary will be copied into /// /// Starting index at which to begin filling the destination array /// public void CopyTo(KeyValuePair[] array, int arrayIndex) { this.typedDictionary.CopyTo(array, arrayIndex); } /// Number of elements contained in the Dictionary public int Count { get { return this.typedDictionary.Count; } } /// Creates a new enumerator for the Dictionary /// The new Dictionary enumerator public IEnumerator> GetEnumerator() { return this.typedDictionary.GetEnumerator(); } /// Collection of all keys contained in the Dictionary public ICollection Keys { get { return this.typedDictionary.Keys; } } /// Collection of all values contained in the Dictionary public ICollection Values { get { return this.typedDictionary.Values; } } /// /// Attempts to retrieve the item with the specified key from the Dictionary /// /// Key of the item to attempt to retrieve /// /// Output parameter that will receive the key upon successful completion /// /// /// True if the item was found and has been placed in the output parameter /// public bool TryGetValue(TKey key, out TValue value) { return this.typedDictionary.TryGetValue(key, out value); } /// Accesses an item in the Dictionary by its key /// Key of the item that will be accessed public TValue this[TKey key] { get { return this.typedDictionary[key]; } set { bool removed; TValue oldValue; removed = this.typedDictionary.TryGetValue(key, out oldValue); this.typedDictionary[key] = value; if(removed) { OnReplaced( new KeyValuePair(key, oldValue), new KeyValuePair(key, value) ); } else { OnAdded(new KeyValuePair(key, value)); } } } /// Inserts an item into the Dictionary /// Key under which to add the new item /// Item that will be added to the Dictionary public void Add(TKey key, TValue value) { this.typedDictionary.Add(key, value); OnAdded(new KeyValuePair(key, value)); } /// Removes the item with the specified key from the Dictionary /// Key of the elementes that will be removed /// True if an item with the specified key was found and removed public bool Remove(TKey key) { TValue oldValue; this.typedDictionary.TryGetValue(key, out oldValue); bool removed = this.typedDictionary.Remove(key); if(removed) { OnRemoved(new KeyValuePair(key, oldValue)); } return removed; } /// Removes all items from the Dictionary public void Clear() { OnClearing(); this.typedDictionary.Clear(); OnCleared(); } /// Fires the 'ItemAdded' event /// Item that has been added to the collection protected virtual void OnAdded(KeyValuePair item) { if(ItemAdded != null) ItemAdded(this, new ItemEventArgs>(item)); #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item) ); } #endif } /// Fires the 'ItemRemoved' event /// Item that has been removed from the collection protected virtual void OnRemoved(KeyValuePair item) { if(ItemRemoved != null) { ItemRemoved(this, new ItemEventArgs>(item)); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item) ); } #endif } /// Fires the 'ItemReplaced' event /// Item that has been replaced in the collection /// Item with which the original item was replaced protected virtual void OnReplaced( KeyValuePair oldItem, KeyValuePair newItem ) { if(ItemReplaced != null) { ItemReplaced( this, new ItemReplaceEventArgs>(oldItem, newItem) ); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Replace, newItem, oldItem ) ); } #endif } /// Fires the 'Clearing' event protected virtual void OnClearing() { if(Clearing != null) { Clearing(this, EventArgs.Empty); } } /// Fires the 'Cleared' event protected virtual void OnCleared() { if(Cleared != null) { Cleared(this, EventArgs.Empty); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged(this, Constants.NotifyCollectionResetEventArgs); } #endif } #region IEnumerable implementation /// Returns a new object enumerator for the Dictionary /// The new object enumerator System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return (this.typedDictionary as IEnumerable).GetEnumerator(); } #endregion #region IDictionary implementation /// Adds an item into the Dictionary /// Key under which the item will be added /// Item that will be added void IDictionary.Add(object key, object value) { this.objectDictionary.Add(key, value); OnAdded(new KeyValuePair((TKey)key, (TValue)value)); } /// Determines whether the specified key exists in the Dictionary /// Key that will be checked for /// True if an item with the specified key exists in the Dictionary bool IDictionary.Contains(object key) { return this.objectDictionary.Contains(key); } /// Returns a new entry enumerator for the dictionary /// The new entry enumerator IDictionaryEnumerator IDictionary.GetEnumerator() { return this.objectDictionary.GetEnumerator(); } /// Whether the size of the Dictionary is fixed bool IDictionary.IsFixedSize { get { return this.objectDictionary.IsFixedSize; } } /// Returns a collection of all keys in the Dictionary ICollection IDictionary.Keys { get { return this.objectDictionary.Keys; } } /// Returns a collection of all values stored in the Dictionary ICollection IDictionary.Values { get { return this.objectDictionary.Values; } } /// Removes an item from the Dictionary /// Key of the item that will be removed void IDictionary.Remove(object key) { TValue value; bool removed = this.typedDictionary.TryGetValue((TKey)key, out value); this.objectDictionary.Remove(key); if(removed) { OnRemoved(new KeyValuePair((TKey)key, (TValue)value)); } } /// Accesses an item in the Dictionary by its key /// Key of the item that will be accessed /// The item with the specified key object IDictionary.this[object key] { get { return this.objectDictionary[key]; } set { bool removed; TValue oldValue; removed = this.typedDictionary.TryGetValue((TKey)key, out oldValue); this.objectDictionary[key] = value; if(removed) { OnRemoved(new KeyValuePair((TKey)key, oldValue)); } OnAdded(new KeyValuePair((TKey)key, (TValue)value)); } } #endregion #region ICollection<> implementation /// Inserts an already prepared element into the Dictionary /// Prepared element that will be added to the Dictionary void ICollection>.Add( KeyValuePair item ) { this.typedDictionary.Add(item); OnAdded(item); } /// Removes all items from the Dictionary void ICollection>.Clear() { OnClearing(); this.typedDictionary.Clear(); OnCleared(); } /// Removes all items from the Dictionary /// Item that will be removed from the Dictionary bool ICollection>.Remove( KeyValuePair itemToRemove ) { bool removed = this.typedDictionary.Remove(itemToRemove); if(removed) { OnRemoved(itemToRemove); } return removed; } #endregion #region ICollection implementation /// Copies the contents of the Dictionary into an array /// Array the Dictionary contents will be copied into /// /// Starting index at which to begin filling the destination array /// void ICollection.CopyTo(Array array, int index) { this.objectDictionary.CopyTo(array, index); } /// Whether the Dictionary is synchronized for multi-threaded usage bool ICollection.IsSynchronized { get { return this.objectDictionary.IsSynchronized; } } /// Synchronization root on which the Dictionary locks object ICollection.SyncRoot { get { return this.objectDictionary.SyncRoot; } } #endregion #if !NO_SERIALIZATION #region ISerializable implementation /// Serializes the Dictionary /// /// Provides the container into which the Dictionary will serialize itself /// /// /// Contextual informations about the serialization environment /// void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { (this.typedDictionary as ISerializable).GetObjectData(info, context); } /// Called after all objects have been successfully deserialized /// Nicht unterstützt void IDeserializationCallback.OnDeserialization(object sender) { (this.typedDictionary as IDeserializationCallback).OnDeserialization(sender); } #endregion #endif //!NO_SERIALIZATION /// The wrapped Dictionary under its type-safe interface private IDictionary typedDictionary; /// The wrapped Dictionary under its object interface private IDictionary objectDictionary; } } // namespace Nuclex.Support.Collections