963 lines
No EOL
28 KiB
C#
963 lines
No EOL
28 KiB
C#
/* Copyright (c) 2007, Dr. WPF
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * The name Dr. WPF may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY Dr. WPF ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL Dr. WPF BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#pragma warning disable 0693
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Runtime.Serialization;
|
|
using System.Runtime.InteropServices;
|
|
using System.Xml.Serialization;
|
|
|
|
namespace System.Collections.Specialized
|
|
{
|
|
[Serializable]
|
|
public class ObservableDictionary<TKey, TValue> :
|
|
IDictionary<TKey, TValue>,
|
|
ICollection<KeyValuePair<TKey, TValue>>,
|
|
IEnumerable<KeyValuePair<TKey, TValue>>,
|
|
IDictionary,
|
|
ICollection,
|
|
IEnumerable,
|
|
ISerializable,
|
|
IXmlSerializable,
|
|
IDeserializationCallback,
|
|
INotifyCollectionChanged,
|
|
INotifyPropertyChanged,
|
|
IEditableObject
|
|
{
|
|
#region constructors
|
|
|
|
#region public
|
|
|
|
public ObservableDictionary()
|
|
{
|
|
_keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>();
|
|
}
|
|
|
|
public ObservableDictionary(IDictionary<TKey, TValue> dictionary)
|
|
{
|
|
_keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>();
|
|
|
|
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
|
|
DoAddEntry((TKey)entry.Key, (TValue)entry.Value);
|
|
}
|
|
|
|
public ObservableDictionary(IEqualityComparer<TKey> comparer)
|
|
{
|
|
_keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>(comparer);
|
|
}
|
|
|
|
public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
|
|
{
|
|
_keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>(comparer);
|
|
|
|
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
|
|
DoAddEntry((TKey)entry.Key, (TValue)entry.Value);
|
|
}
|
|
|
|
#endregion public
|
|
|
|
#region protected
|
|
|
|
protected ObservableDictionary(SerializationInfo info, StreamingContext context)
|
|
{
|
|
_siInfo = info;
|
|
}
|
|
|
|
#endregion protected
|
|
|
|
#endregion constructors
|
|
|
|
#region properties
|
|
|
|
#region public
|
|
|
|
public IEqualityComparer<TKey> Comparer
|
|
{
|
|
get { return _keyedEntryCollection.Comparer; }
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get { return _keyedEntryCollection.Count; }
|
|
}
|
|
|
|
public Dictionary<TKey, TValue>.KeyCollection Keys
|
|
{
|
|
get { return TrueDictionary.Keys; }
|
|
}
|
|
|
|
public TValue this[TKey key]
|
|
{
|
|
get { return (TValue)_keyedEntryCollection[key].Value; }
|
|
set { DoSetEntry(key, value); }
|
|
}
|
|
public TValue this[int index]
|
|
{
|
|
get
|
|
{
|
|
if (_keyedEntryCollection.Count <= index)
|
|
throw new IndexOutOfRangeException();
|
|
return (TValue)_keyedEntryCollection[index].Value;
|
|
}
|
|
set
|
|
{
|
|
if (_keyedEntryCollection.Count <= index)
|
|
throw new IndexOutOfRangeException();
|
|
DoSetEntry(index, value);
|
|
}
|
|
}
|
|
|
|
public Dictionary<TKey, TValue>.ValueCollection Values
|
|
{
|
|
get { return TrueDictionary.Values; }
|
|
}
|
|
|
|
#endregion public
|
|
|
|
#region private
|
|
|
|
private Dictionary<TKey, TValue> TrueDictionary
|
|
{
|
|
get
|
|
{
|
|
if (_dictionaryCacheVersion != _version)
|
|
{
|
|
_dictionaryCache.Clear();
|
|
foreach (DictionaryEntry entry in _keyedEntryCollection)
|
|
_dictionaryCache.Add((TKey)entry.Key, (TValue)entry.Value);
|
|
_dictionaryCacheVersion = _version;
|
|
}
|
|
return _dictionaryCache;
|
|
}
|
|
}
|
|
|
|
#endregion private
|
|
|
|
#endregion properties
|
|
|
|
#region methods
|
|
|
|
#region public
|
|
|
|
public void Add(TKey key, TValue value)
|
|
{
|
|
DoAddEntry(key, value);
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
DoClearEntries();
|
|
}
|
|
|
|
public bool ContainsKey(TKey key)
|
|
{
|
|
return _keyedEntryCollection.Contains(key);
|
|
}
|
|
|
|
public bool ContainsValue(TValue value)
|
|
{
|
|
return TrueDictionary.ContainsValue(value);
|
|
}
|
|
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
return new Enumerator<TKey, TValue>(this, false);
|
|
}
|
|
|
|
public bool Remove(TKey key)
|
|
{
|
|
return DoRemoveEntry(key);
|
|
}
|
|
|
|
public bool TryGetValue(TKey key, out TValue value)
|
|
{
|
|
bool result = _keyedEntryCollection.Contains(key);
|
|
value = result ? (TValue)_keyedEntryCollection[key].Value : default(TValue);
|
|
return result;
|
|
}
|
|
|
|
void IEditableObject.BeginEdit()
|
|
{
|
|
if (!inTxn)
|
|
{
|
|
inTxn = true;
|
|
this._backupKDEC = new KeyedDictionaryEntryCollection<TKey>(this._keyedEntryCollection);
|
|
}
|
|
}
|
|
|
|
void IEditableObject.CancelEdit()
|
|
{
|
|
if (inTxn)
|
|
{
|
|
inTxn = false;
|
|
this._keyedEntryCollection = this._backupKDEC;
|
|
}
|
|
}
|
|
|
|
void IEditableObject.EndEdit()
|
|
{
|
|
if (inTxn)
|
|
{
|
|
inTxn = false;
|
|
this._backupKDEC = null;
|
|
}
|
|
}
|
|
|
|
#endregion public
|
|
|
|
#region protected
|
|
|
|
protected virtual bool AddEntry(TKey key, TValue value)
|
|
{
|
|
_keyedEntryCollection.Add(new DictionaryEntry(key, value));
|
|
return true;
|
|
}
|
|
|
|
protected virtual bool ClearEntries()
|
|
{
|
|
// check whether there are entries to clear
|
|
bool result = (Count > 0);
|
|
if (result)
|
|
{
|
|
// if so, clear the dictionary
|
|
_keyedEntryCollection.Clear();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected int GetIndexAndEntryForKey(TKey key, out DictionaryEntry entry)
|
|
{
|
|
entry = new DictionaryEntry();
|
|
int index = -1;
|
|
if (_keyedEntryCollection.Contains(key))
|
|
{
|
|
entry = _keyedEntryCollection[key];
|
|
index = _keyedEntryCollection.IndexOf(entry);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
|
{
|
|
if (CollectionChanged != null)
|
|
CollectionChanged(this, args);
|
|
}
|
|
|
|
protected virtual void OnPropertyChanged(string name)
|
|
{
|
|
if (PropertyChanged != null)
|
|
PropertyChanged(this, new PropertyChangedEventArgs(name));
|
|
}
|
|
|
|
protected virtual bool RemoveEntry(TKey key)
|
|
{
|
|
// remove the entry
|
|
return _keyedEntryCollection.Remove(key);
|
|
}
|
|
|
|
protected virtual bool SetEntry(TKey key, TValue value)
|
|
{
|
|
bool keyExists = _keyedEntryCollection.Contains(key);
|
|
|
|
// if identical key/value pair already exists, nothing to do
|
|
if (keyExists && value.Equals((TValue)_keyedEntryCollection[key].Value))
|
|
return false;
|
|
|
|
// otherwise, remove the existing entry
|
|
if (keyExists)
|
|
{
|
|
int index = _keyedEntryCollection.IndexOf(_keyedEntryCollection[key]);
|
|
_keyedEntryCollection.Remove(key);
|
|
// add the new entry
|
|
_keyedEntryCollection.Insert(index, new DictionaryEntry(key, value));
|
|
}
|
|
else
|
|
{
|
|
// add the new entry
|
|
_keyedEntryCollection.Add(new DictionaryEntry(key, value));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion protected
|
|
|
|
#region private
|
|
|
|
private void DoAddEntry(TKey key, TValue value)
|
|
{
|
|
if (AddEntry(key, value))
|
|
{
|
|
_version++;
|
|
|
|
DictionaryEntry entry;
|
|
int index = GetIndexAndEntryForKey(key, out entry);
|
|
FireEntryAddedNotifications(entry, index);
|
|
}
|
|
}
|
|
|
|
private void DoClearEntries()
|
|
{
|
|
if (ClearEntries())
|
|
{
|
|
_version++;
|
|
FireResetNotifications();
|
|
}
|
|
}
|
|
|
|
private bool DoRemoveEntry(TKey key)
|
|
{
|
|
DictionaryEntry entry;
|
|
int index = GetIndexAndEntryForKey(key, out entry);
|
|
|
|
bool result = RemoveEntry(key);
|
|
if (result)
|
|
{
|
|
_version++;
|
|
if (index > -1)
|
|
FireEntryRemovedNotifications(entry, index);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private void DoSetEntry(int index, TValue value)
|
|
{
|
|
DictionaryEntry entry = _keyedEntryCollection[index];
|
|
DoSetEntry(index, entry, value);
|
|
}
|
|
private void DoSetEntry(TKey key, TValue value)
|
|
{
|
|
DictionaryEntry entry;
|
|
int index = GetIndexAndEntryForKey(key, out entry);
|
|
DoSetEntry(index, entry, value);
|
|
}
|
|
private void DoSetEntry(int index, DictionaryEntry entry, TValue value)
|
|
{
|
|
if (SetEntry((TKey)entry.Key, value))
|
|
{
|
|
_version++;
|
|
|
|
// if prior entry existed for this key, fire the removed notifications
|
|
if (index > -1)
|
|
{
|
|
FireEntryRemovedNotifications(entry, index);
|
|
|
|
// force the property change notifications to fire for the modified entry
|
|
_countCache--;
|
|
}
|
|
|
|
// then fire the added notifications
|
|
index = GetIndexAndEntryForKey((TKey)entry.Key, out entry);
|
|
FireEntryAddedNotifications(entry, index);
|
|
}
|
|
}
|
|
|
|
private void FireEntryAddedNotifications(DictionaryEntry entry, int index)
|
|
{
|
|
// fire the relevant PropertyChanged notifications
|
|
FirePropertyChangedNotifications();
|
|
|
|
// fire CollectionChanged notification
|
|
if (index > -1)
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value), index));
|
|
else
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
private void FireEntryRemovedNotifications(DictionaryEntry entry, int index)
|
|
{
|
|
// fire the relevant PropertyChanged notifications
|
|
FirePropertyChangedNotifications();
|
|
|
|
// fire CollectionChanged notification
|
|
if (index > -1)
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value), index));
|
|
else
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
private void FirePropertyChangedNotifications()
|
|
{
|
|
if (Count != _countCache)
|
|
{
|
|
_countCache = Count;
|
|
OnPropertyChanged("Count");
|
|
OnPropertyChanged("Item[]");
|
|
OnPropertyChanged("Keys");
|
|
OnPropertyChanged("Values");
|
|
}
|
|
}
|
|
|
|
private void FireResetNotifications()
|
|
{
|
|
// fire the relevant PropertyChanged notifications
|
|
FirePropertyChangedNotifications();
|
|
|
|
// fire CollectionChanged notification
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
#endregion private
|
|
|
|
#endregion methods
|
|
|
|
#region interfaces
|
|
|
|
#region IDictionary<TKey, TValue>
|
|
|
|
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
|
|
{
|
|
DoAddEntry(key, value);
|
|
}
|
|
|
|
bool IDictionary<TKey, TValue>.Remove(TKey key)
|
|
{
|
|
return DoRemoveEntry(key);
|
|
}
|
|
|
|
bool IDictionary<TKey, TValue>.ContainsKey(TKey key)
|
|
{
|
|
return _keyedEntryCollection.Contains(key);
|
|
}
|
|
|
|
bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value)
|
|
{
|
|
return TryGetValue(key, out value);
|
|
}
|
|
|
|
ICollection<TKey> IDictionary<TKey, TValue>.Keys
|
|
{
|
|
get { return Keys; }
|
|
}
|
|
|
|
ICollection<TValue> IDictionary<TKey, TValue>.Values
|
|
{
|
|
get { return Values; }
|
|
}
|
|
|
|
TValue IDictionary<TKey, TValue>.this[TKey key]
|
|
{
|
|
get { return (TValue)_keyedEntryCollection[key].Value; }
|
|
set { DoSetEntry(key, value); }
|
|
}
|
|
|
|
#endregion IDictionary<TKey, TValue>
|
|
|
|
#region IDictionary
|
|
|
|
void IDictionary.Add(object key, object value)
|
|
{
|
|
DoAddEntry((TKey)key, (TValue)value);
|
|
}
|
|
|
|
void IDictionary.Clear()
|
|
{
|
|
DoClearEntries();
|
|
}
|
|
|
|
bool IDictionary.Contains(object key)
|
|
{
|
|
return _keyedEntryCollection.Contains((TKey)key);
|
|
}
|
|
|
|
IDictionaryEnumerator IDictionary.GetEnumerator()
|
|
{
|
|
return new Enumerator<TKey, TValue>(this, true);
|
|
}
|
|
|
|
bool IDictionary.IsFixedSize
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
bool IDictionary.IsReadOnly
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
object IDictionary.this[object key]
|
|
{
|
|
get { return _keyedEntryCollection[(TKey)key].Value; }
|
|
set { DoSetEntry((TKey)key, (TValue)value); }
|
|
}
|
|
|
|
ICollection IDictionary.Keys
|
|
{
|
|
get { return Keys; }
|
|
}
|
|
|
|
void IDictionary.Remove(object key)
|
|
{
|
|
DoRemoveEntry((TKey)key);
|
|
}
|
|
|
|
ICollection IDictionary.Values
|
|
{
|
|
get { return Values; }
|
|
}
|
|
|
|
#endregion IDictionary
|
|
|
|
#region ICollection<KeyValuePair<TKey, TValue>>
|
|
|
|
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> kvp)
|
|
{
|
|
DoAddEntry(kvp.Key, kvp.Value);
|
|
}
|
|
|
|
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
|
|
{
|
|
DoClearEntries();
|
|
}
|
|
|
|
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> kvp)
|
|
{
|
|
return _keyedEntryCollection.Contains(kvp.Key);
|
|
}
|
|
|
|
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
|
|
{
|
|
if (array == null)
|
|
{
|
|
throw new ArgumentNullException("CopyTo() failed: array parameter was null");
|
|
}
|
|
if ((index < 0) || (index > array.Length))
|
|
{
|
|
throw new ArgumentOutOfRangeException("CopyTo() failed: index parameter was outside the bounds of the supplied array");
|
|
}
|
|
if ((array.Length - index) < _keyedEntryCollection.Count)
|
|
{
|
|
throw new ArgumentException("CopyTo() failed: supplied array was too small");
|
|
}
|
|
|
|
foreach (DictionaryEntry entry in _keyedEntryCollection)
|
|
array[index++] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
|
|
}
|
|
|
|
int ICollection<KeyValuePair<TKey, TValue>>.Count
|
|
{
|
|
get { return _keyedEntryCollection.Count; }
|
|
}
|
|
|
|
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> kvp)
|
|
{
|
|
return DoRemoveEntry(kvp.Key);
|
|
}
|
|
|
|
#endregion ICollection<KeyValuePair<TKey, TValue>>
|
|
|
|
#region ICollection
|
|
|
|
void ICollection.CopyTo(Array array, int index)
|
|
{
|
|
((ICollection)_keyedEntryCollection).CopyTo(array, index);
|
|
}
|
|
|
|
int ICollection.Count
|
|
{
|
|
get { return _keyedEntryCollection.Count; }
|
|
}
|
|
|
|
bool ICollection.IsSynchronized
|
|
{
|
|
get { return ((ICollection)_keyedEntryCollection).IsSynchronized; }
|
|
}
|
|
|
|
object ICollection.SyncRoot
|
|
{
|
|
get { return ((ICollection)_keyedEntryCollection).SyncRoot; }
|
|
}
|
|
|
|
#endregion ICollection
|
|
|
|
#region IEnumerable<KeyValuePair<TKey, TValue>>
|
|
|
|
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
|
|
{
|
|
return new Enumerator<TKey, TValue>(this, false);
|
|
}
|
|
|
|
#endregion IEnumerable<KeyValuePair<TKey, TValue>>
|
|
|
|
#region IEnumerable
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
#endregion IEnumerable
|
|
|
|
#region ISerializable
|
|
|
|
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
if (info == null)
|
|
{
|
|
throw new ArgumentNullException("info");
|
|
}
|
|
|
|
Collection<DictionaryEntry> entries = new Collection<DictionaryEntry>();
|
|
foreach (DictionaryEntry entry in _keyedEntryCollection)
|
|
entries.Add(entry);
|
|
info.AddValue("entries", entries);
|
|
}
|
|
|
|
#endregion ISerializable
|
|
|
|
#region IXmlSerializable
|
|
public System.Xml.Schema.XmlSchema GetSchema()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public void ReadXml(System.Xml.XmlReader reader)
|
|
{
|
|
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
|
|
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
|
|
|
|
bool wasEmpty = reader.IsEmptyElement;
|
|
reader.Read();
|
|
|
|
if (wasEmpty)
|
|
return;
|
|
|
|
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
|
|
{
|
|
reader.ReadStartElement("item");
|
|
|
|
reader.ReadStartElement("key");
|
|
TKey key = (TKey)keySerializer.Deserialize(reader);
|
|
reader.ReadEndElement();
|
|
|
|
reader.ReadStartElement("value");
|
|
TValue value = (TValue)valueSerializer.Deserialize(reader);
|
|
reader.ReadEndElement();
|
|
|
|
this.Add(key, value);
|
|
|
|
reader.ReadEndElement();
|
|
reader.MoveToContent();
|
|
}
|
|
reader.ReadEndElement();
|
|
}
|
|
|
|
public void WriteXml(System.Xml.XmlWriter writer)
|
|
{
|
|
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
|
|
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
|
|
|
|
foreach (TKey key in this.Keys)
|
|
{
|
|
writer.WriteStartElement("item");
|
|
|
|
writer.WriteStartElement("key");
|
|
keySerializer.Serialize(writer, key);
|
|
writer.WriteEndElement();
|
|
|
|
writer.WriteStartElement("value");
|
|
TValue value = this[key];
|
|
valueSerializer.Serialize(writer, value);
|
|
writer.WriteEndElement();
|
|
|
|
writer.WriteEndElement();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region IDeserializationCallback
|
|
|
|
public virtual void OnDeserialization(object sender)
|
|
{
|
|
if (_siInfo != null)
|
|
{
|
|
Collection<DictionaryEntry> entries = (Collection<DictionaryEntry>)
|
|
_siInfo.GetValue("entries", typeof(Collection<DictionaryEntry>));
|
|
foreach (DictionaryEntry entry in entries)
|
|
AddEntry((TKey)entry.Key, (TValue)entry.Value);
|
|
}
|
|
}
|
|
|
|
#endregion IDeserializationCallback
|
|
|
|
#region INotifyCollectionChanged
|
|
|
|
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
|
|
{
|
|
add { CollectionChanged += value; }
|
|
remove { CollectionChanged -= value; }
|
|
}
|
|
|
|
protected virtual event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
#endregion INotifyCollectionChanged
|
|
|
|
#region INotifyPropertyChanged
|
|
|
|
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
|
|
{
|
|
add { PropertyChanged += value; }
|
|
remove { PropertyChanged -= value; }
|
|
}
|
|
|
|
protected virtual event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
#endregion INotifyPropertyChanged
|
|
|
|
#endregion interfaces
|
|
|
|
#region protected classes
|
|
|
|
#region KeyedDictionaryEntryCollection<TKey>
|
|
|
|
protected class KeyedDictionaryEntryCollection<TKey> : KeyedCollection<TKey, DictionaryEntry>
|
|
{
|
|
#region constructors
|
|
|
|
#region public
|
|
|
|
public KeyedDictionaryEntryCollection() : base() { }
|
|
|
|
public KeyedDictionaryEntryCollection(IEqualityComparer<TKey> comparer) : base(comparer) { }
|
|
|
|
public KeyedDictionaryEntryCollection(KeyedDictionaryEntryCollection<TKey> kdec) : base()
|
|
{
|
|
foreach (DictionaryEntry de in kdec)
|
|
this.Add(de);
|
|
}
|
|
|
|
#endregion public
|
|
|
|
#endregion constructors
|
|
|
|
#region methods
|
|
|
|
#region protected
|
|
|
|
protected override TKey GetKeyForItem(DictionaryEntry entry)
|
|
{
|
|
return (TKey)entry.Key;
|
|
}
|
|
|
|
#endregion protected
|
|
|
|
#endregion methods
|
|
}
|
|
|
|
#endregion KeyedDictionaryEntryCollection<TKey>
|
|
|
|
#endregion protected classes
|
|
|
|
#region public structures
|
|
|
|
#region Enumerator
|
|
|
|
[Serializable, StructLayout(LayoutKind.Sequential)]
|
|
public struct Enumerator<TKey, TValue> : IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, IDictionaryEnumerator, IEnumerator
|
|
{
|
|
#region constructors
|
|
|
|
internal Enumerator(ObservableDictionary<TKey, TValue> dictionary, bool isDictionaryEntryEnumerator)
|
|
{
|
|
_dictionary = dictionary;
|
|
_version = dictionary._version;
|
|
_index = -1;
|
|
_isDictionaryEntryEnumerator = isDictionaryEntryEnumerator;
|
|
_current = new KeyValuePair<TKey, TValue>();
|
|
}
|
|
|
|
#endregion constructors
|
|
|
|
#region properties
|
|
|
|
#region public
|
|
|
|
public KeyValuePair<TKey, TValue> Current
|
|
{
|
|
get
|
|
{
|
|
ValidateCurrent();
|
|
return _current;
|
|
}
|
|
}
|
|
|
|
#endregion public
|
|
|
|
#endregion properties
|
|
|
|
#region methods
|
|
|
|
#region public
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
public bool MoveNext()
|
|
{
|
|
ValidateVersion();
|
|
_index++;
|
|
if (_index < _dictionary._keyedEntryCollection.Count)
|
|
{
|
|
_current = new KeyValuePair<TKey, TValue>((TKey)_dictionary._keyedEntryCollection[_index].Key, (TValue)_dictionary._keyedEntryCollection[_index].Value);
|
|
return true;
|
|
}
|
|
_index = -2;
|
|
_current = new KeyValuePair<TKey, TValue>();
|
|
return false;
|
|
}
|
|
|
|
#endregion public
|
|
|
|
#region private
|
|
|
|
private void ValidateCurrent()
|
|
{
|
|
if (_index == -1)
|
|
{
|
|
throw new InvalidOperationException("The enumerator has not been started.");
|
|
}
|
|
else if (_index == -2)
|
|
{
|
|
throw new InvalidOperationException("The enumerator has reached the end of the collection.");
|
|
}
|
|
}
|
|
|
|
private void ValidateVersion()
|
|
{
|
|
if (_version != _dictionary._version)
|
|
{
|
|
throw new InvalidOperationException("The enumerator is not valid because the dictionary changed.");
|
|
}
|
|
}
|
|
|
|
#endregion private
|
|
|
|
#endregion methods
|
|
|
|
#region IEnumerator implementation
|
|
|
|
object IEnumerator.Current
|
|
{
|
|
get
|
|
{
|
|
ValidateCurrent();
|
|
if (_isDictionaryEntryEnumerator)
|
|
{
|
|
return new DictionaryEntry(_current.Key, _current.Value);
|
|
}
|
|
return new KeyValuePair<TKey, TValue>(_current.Key, _current.Value);
|
|
}
|
|
}
|
|
|
|
void IEnumerator.Reset()
|
|
{
|
|
ValidateVersion();
|
|
_index = -1;
|
|
_current = new KeyValuePair<TKey, TValue>();
|
|
}
|
|
|
|
#endregion IEnumerator implemenation
|
|
|
|
#region IDictionaryEnumerator implemenation
|
|
|
|
DictionaryEntry IDictionaryEnumerator.Entry
|
|
{
|
|
get
|
|
{
|
|
ValidateCurrent();
|
|
return new DictionaryEntry(_current.Key, _current.Value);
|
|
}
|
|
}
|
|
object IDictionaryEnumerator.Key
|
|
{
|
|
get
|
|
{
|
|
ValidateCurrent();
|
|
return _current.Key;
|
|
}
|
|
}
|
|
object IDictionaryEnumerator.Value
|
|
{
|
|
get
|
|
{
|
|
ValidateCurrent();
|
|
return _current.Value;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region fields
|
|
|
|
private ObservableDictionary<TKey, TValue> _dictionary;
|
|
private int _version;
|
|
private int _index;
|
|
private KeyValuePair<TKey, TValue> _current;
|
|
private bool _isDictionaryEntryEnumerator;
|
|
|
|
#endregion fields
|
|
}
|
|
|
|
#endregion Enumerator
|
|
|
|
#endregion public structures
|
|
|
|
#region fields
|
|
|
|
protected KeyedDictionaryEntryCollection<TKey> _keyedEntryCollection;
|
|
|
|
private int _countCache = 0;
|
|
private Dictionary<TKey, TValue> _dictionaryCache = new Dictionary<TKey, TValue>();
|
|
private int _dictionaryCacheVersion = 0;
|
|
private int _version = 0;
|
|
|
|
[NonSerialized]
|
|
private SerializationInfo _siInfo = null;
|
|
[NonSerialized]
|
|
private KeyedDictionaryEntryCollection<TKey> _backupKDEC;
|
|
[NonSerialized]
|
|
private bool inTxn = false;
|
|
|
|
#endregion fields
|
|
}
|
|
}
|
|
|
|
#pragma warning restore |