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 ) );
}
}