#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