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;
private readonly IList> _BaseCollection;
private readonly IDictionary _IndexDictionary;
protected IDictionary Dictionary
{
get { return _Dictionary; }
}
#region Constructors
public ObservableDictionary()
{
_Dictionary = new Dictionary();
this._IndexDictionary = new Dictionary();
this._BaseCollection = new List>();
}
public ObservableDictionary(IDictionary dictionary)
{
_Dictionary = new Dictionary(dictionary);
this._IndexDictionary = new Dictionary();
this._BaseCollection = new List>();
if (dictionary != null)
foreach (var pair in dictionary)
((IDictionary)this).Add(pair);
}
public ObservableDictionary(IEqualityComparer comparer)
{
_Dictionary = new Dictionary(comparer);
this._IndexDictionary = new Dictionary();
this._BaseCollection = new List>();
}
public ObservableDictionary(int capacity)
{
_Dictionary = new Dictionary(capacity);
this._IndexDictionary = new Dictionary();
this._BaseCollection = new List>();
}
public ObservableDictionary(IDictionary dictionary, IEqualityComparer comparer)
{
_Dictionary = new Dictionary(dictionary, comparer);
this._IndexDictionary = new Dictionary();
this._BaseCollection = new List>();
if (dictionary != null)
foreach (var pair in dictionary)
((IDictionary)this).Add(pair);
}
public ObservableDictionary(int capacity, IEqualityComparer comparer)
{
_Dictionary = new Dictionary(capacity, comparer);
this._IndexDictionary = new Dictionary();
this._BaseCollection = new List>();
}
#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)
{
int index = this._IndexDictionary[key];
this._BaseCollection.RemoveAt(index);
for (int x = index; x < this._BaseCollection.Count; x++)
this._IndexDictionary[this._BaseCollection[x].Key] = x;
this._IndexDictionary.Remove(key);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, new KeyValuePair(key, value), index);
}
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();
this._BaseCollection.Clear();
this._IndexDictionary.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
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
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);
this._BaseCollection.Add(item);
this._IndexDictionary[item.Key] = this._BaseCollection.Count - 1;
}
}
else
{
_Dictionary = new Dictionary(items);
this._IndexDictionary.Clear();
this._BaseCollection.Clear();
foreach (var pair in items)
{
this._BaseCollection.Add(pair);
this._IndexDictionary[pair.Key] = this._BaseCollection.Count - 1;
}
}
OnCollectionChanged(NotifyCollectionChangedAction.Add, items.ToArray(), 0);
}
}
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;
this._BaseCollection[this._IndexDictionary[key]] = new KeyValuePair(key, value);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, new KeyValuePair(key, value), new KeyValuePair(key, item), this._IndexDictionary[key]);
}
else
{
Dictionary[key] = value;
this._BaseCollection.Add(new KeyValuePair(key, value));
this._IndexDictionary[key] = this._BaseCollection.Count - 1;
OnCollectionChanged(NotifyCollectionChangedAction.Add, new KeyValuePair(key, value), this._IndexDictionary[key]);
}
}
private void OnPropertyChanged()
{
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnPropertyChanged(KeysName);
OnPropertyChanged(ValuesName);
}
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, int index)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, changedItem, index));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair newItem, KeyValuePair oldItem, int index)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems, int index)
{
OnPropertyChanged();
if (CollectionChanged != null) CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newItems, index));
}
}