Advertisement
Guest User

ObservableDictionary

a guest
Aug 1st, 2016
144
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 20.26 KB | None | 0 0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Collections.Specialized;
  6. using System.ComponentModel;
  7. using System.Runtime.InteropServices;
  8. using System.Runtime.Serialization;
  9. using System.Xml;
  10. using System.Xml.Serialization;
  11.  
  12. namespace FooBar
  13. {
  14.     [Serializable]
  15.     public class ObservableDictionary<TKey, TVal> :
  16.         IDictionary<TKey, TVal>,
  17.         ICollection,
  18.         IXmlSerializable,
  19.         ISerializable,
  20.         INotifyCollectionChanged,
  21.         INotifyPropertyChanged
  22.     {
  23.         private const string ItemNodeName = "Item";
  24.         private const string KeyNodeName = "Key";
  25.         private const string ValueNodeName = "Value";
  26.  
  27.         protected KeyedDictionaryEntryCollection<TKey> keyedEntryCollection;
  28.         private int countCache = 0;
  29.         private Dictionary<TKey, TVal> dictionaryCache = new Dictionary<TKey, TVal>();
  30.         private int dictionaryCacheVersion = 0;
  31.         private int version = 0;
  32.         private XmlSerializer keySerializer = null;
  33.         private XmlSerializer valueSerializer = null;
  34.  
  35.  
  36.         public ObservableDictionary()
  37.         {
  38.             this.keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>();
  39.         }
  40.  
  41.         public ObservableDictionary(IDictionary<TKey, TVal> dictionary)
  42.         {
  43.             this.keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>();
  44.  
  45.             foreach (KeyValuePair<TKey, TVal> entry in dictionary)
  46.                 this.DoAddEntry((TKey)entry.Key, (TVal)entry.Value);
  47.         }
  48.  
  49.         public ObservableDictionary(IEqualityComparer<TKey> comparer)
  50.         {
  51.             this.keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>(comparer);
  52.         }
  53.  
  54.         public ObservableDictionary(IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer)
  55.         {
  56.             this.keyedEntryCollection = new KeyedDictionaryEntryCollection<TKey>(comparer);
  57.  
  58.             foreach (KeyValuePair<TKey, TVal> entry in dictionary)
  59.                 this.DoAddEntry((TKey)entry.Key, (TVal)entry.Value);
  60.         }
  61.  
  62.         protected ObservableDictionary(SerializationInfo info, StreamingContext context)
  63.         {
  64.             int itemCount = info.GetInt32("ItemCount");
  65.             for (int i = 0; i < itemCount; i++)
  66.             {
  67.                 KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format("Item{0}", i), typeof(KeyValuePair<TKey, TVal>));
  68.                 this.Add(kvp.Key, kvp.Value);
  69.             }
  70.         }
  71.  
  72.  
  73.         public IEqualityComparer<TKey> Comparer
  74.         {
  75.             get { return this.keyedEntryCollection.Comparer; }
  76.         }
  77.  
  78.         public int Count
  79.         {
  80.             get { return this.keyedEntryCollection.Count; }
  81.         }
  82.  
  83.         public Dictionary<TKey, TVal>.KeyCollection Keys
  84.         {
  85.             get { return this.TrueDictionary.Keys; }
  86.         }
  87.  
  88.         ICollection<TKey> IDictionary<TKey, TVal>.Keys
  89.         {
  90.             get
  91.             {
  92.                 return Keys;
  93.             }
  94.         }
  95.  
  96.         public TVal this[TKey key]
  97.         {
  98.             get { return (TVal)this.keyedEntryCollection[key].Value; }
  99.             set { this.DoSetEntry(key, value); }
  100.         }
  101.  
  102.         public Dictionary<TKey, TVal>.ValueCollection Values
  103.         {
  104.             get { return this.TrueDictionary.Values; }
  105.         }
  106.  
  107.         ICollection<TVal> IDictionary<TKey, TVal>.Values
  108.         {
  109.             get
  110.             {
  111.                 return Values;
  112.             }
  113.         }
  114.  
  115.         public bool IsReadOnly
  116.         {
  117.             get
  118.             {
  119.                 return false;
  120.             }
  121.         }
  122.  
  123.         private Dictionary<TKey, TVal> TrueDictionary
  124.         {
  125.             get
  126.             {
  127.                 if (this.dictionaryCacheVersion != version)
  128.                 {
  129.                     this.dictionaryCache.Clear();
  130.                     foreach (DictionaryEntry entry in keyedEntryCollection)
  131.                         this.dictionaryCache.Add((TKey)entry.Key, (TVal)entry.Value);
  132.                     this.dictionaryCacheVersion = version;
  133.                 }
  134.                 return this.dictionaryCache;
  135.             }
  136.         }
  137.  
  138.         public void Add(TKey key, TVal value)
  139.         {
  140.             this.DoAddEntry(key, value);
  141.         }
  142.  
  143.         public void Add(KeyValuePair<TKey, TVal> item)
  144.         {
  145.             this.DoAddEntry(item.Key, item.Value);
  146.         }
  147.  
  148.         public void Clear()
  149.         {
  150.             this.DoClearEntries();
  151.         }
  152.  
  153.         public bool Contains(KeyValuePair<TKey, TVal> item)
  154.         {
  155.             return this.keyedEntryCollection.Contains(item.Key);
  156.         }
  157.  
  158.         public bool ContainsKey(TKey key)
  159.         {
  160.             return this.keyedEntryCollection.Contains(key);
  161.         }
  162.  
  163.         public bool ContainsValue(TVal value)
  164.         {
  165.             return this.TrueDictionary.ContainsValue(value);
  166.         }
  167.  
  168.         public IEnumerator GetEnumerator()
  169.         {
  170.             return new Enumerator<TKey, TVal>(this, false);
  171.         }
  172.  
  173.         public bool Remove(TKey key)
  174.         {
  175.             return this.DoRemoveEntry(key);
  176.         }
  177.  
  178.         public bool Remove(KeyValuePair<TKey, TVal> item)
  179.         {
  180.             return this.DoRemoveEntry(item.Key);
  181.         }
  182.  
  183.         public bool TryGetValue(TKey key, out TVal value)
  184.         {
  185.             bool result = this.keyedEntryCollection.Contains(key);
  186.             value = result ? (TVal)this.keyedEntryCollection[key].Value : default(TVal);
  187.             return result;
  188.         }
  189.  
  190.         public object SyncRoot
  191.         {
  192.             get
  193.             {
  194.                 return ((ICollection)this.keyedEntryCollection).SyncRoot;
  195.             }
  196.         }
  197.  
  198.         public bool IsSynchronized
  199.         {
  200.             get
  201.             {
  202.                 return ((ICollection)this.keyedEntryCollection).IsSynchronized;
  203.             }
  204.         }
  205.  
  206.         IEnumerator<KeyValuePair<TKey, TVal>> IEnumerable<KeyValuePair<TKey, TVal>>.GetEnumerator()
  207.         {
  208.             return new Enumerator<TKey, TVal>(this, false);
  209.         }
  210.  
  211.         public void CopyTo(KeyValuePair<TKey, TVal>[] array, int arrayIndex)
  212.         {
  213.             if (array == null)
  214.             {
  215.                 throw new ArgumentNullException("CopyTo() failed:  array parameter was null");
  216.             }
  217.             if ((arrayIndex < 0) || (arrayIndex > array.Length))
  218.             {
  219.                 throw new ArgumentOutOfRangeException("CopyTo() failed:  index parameter was outside the bounds of the supplied array");
  220.             }
  221.             if ((array.Length - arrayIndex) < this.keyedEntryCollection.Count)
  222.             {
  223.                 throw new ArgumentException("CopyTo() failed:  supplied array was too small");
  224.             }
  225.  
  226.             foreach (DictionaryEntry entry in this.keyedEntryCollection)
  227.                 array[arrayIndex++] = new KeyValuePair<TKey, TVal>((TKey)entry.Key, (TVal)entry.Value);
  228.         }
  229.  
  230.         public void CopyTo(Array array, int index)
  231.         {
  232.             ((ICollection)this.keyedEntryCollection).CopyTo(array, index);
  233.         }
  234.  
  235.         protected virtual bool AddEntry(TKey key, TVal value)
  236.         {
  237.             this.keyedEntryCollection.Add(new DictionaryEntry(key, value));
  238.             return true;
  239.         }
  240.  
  241.         protected virtual bool ClearEntries()
  242.         {
  243.             bool entriesToClear = (Count > 0);
  244.             if (entriesToClear)
  245.             {
  246.                 this.keyedEntryCollection.Clear();
  247.             }
  248.             return entriesToClear;
  249.         }
  250.  
  251.         protected int GetIndexAndEntryForKey(TKey key, out DictionaryEntry entry)
  252.         {
  253.             entry = new DictionaryEntry();
  254.             int index = -1;
  255.             if (this.keyedEntryCollection.Contains(key))
  256.             {
  257.                 entry = this.keyedEntryCollection[key];
  258.                 index = this.keyedEntryCollection.IndexOf(entry);
  259.             }
  260.             return index;
  261.         }
  262.  
  263.         protected virtual bool RemoveEntry(TKey key)
  264.         {
  265.             return this.keyedEntryCollection.Remove(key);
  266.         }
  267.  
  268.         protected virtual bool SetEntry(TKey key, TVal value)
  269.         {
  270.             bool keyExists = this.keyedEntryCollection.Contains(key);
  271.  
  272.             // if identical key/value pair already exists, nothing to do
  273.             if (keyExists && value.Equals((TVal)this.keyedEntryCollection[key].Value))
  274.                 return false;
  275.  
  276.             // otherwise, remove the existing entry
  277.             if (keyExists)
  278.                 this.keyedEntryCollection.Remove(key);
  279.  
  280.             // add the new entry
  281.             this.keyedEntryCollection.Add(new DictionaryEntry(key, value));
  282.  
  283.             return true;
  284.         }
  285.  
  286.         private void DoAddEntry(TKey key, TVal value)
  287.         {
  288.             if (this.AddEntry(key, value))
  289.             {
  290.                 version++;
  291.  
  292.                 DictionaryEntry entry;
  293.                 int index = this.GetIndexAndEntryForKey(key, out entry);
  294.                 this.FireEntryAddedNotifications(entry, index);
  295.             }
  296.         }
  297.  
  298.         private void DoClearEntries()
  299.         {
  300.             if (this.ClearEntries())
  301.             {
  302.                 version++;
  303.                 this.FireResetNotifications();
  304.             }
  305.         }
  306.  
  307.         private bool DoRemoveEntry(TKey key)
  308.         {
  309.             DictionaryEntry entry;
  310.             int index = this.GetIndexAndEntryForKey(key, out entry);
  311.  
  312.             bool result = this.RemoveEntry(key);
  313.             if (result)
  314.             {
  315.                 version++;
  316.                 if (index > -1)
  317.                     this.FireEntryRemovedNotifications(entry, index);
  318.             }
  319.  
  320.             return result;
  321.         }
  322.  
  323.         private void DoSetEntry(TKey key, TVal value)
  324.         {
  325.             DictionaryEntry entry;
  326.             int index = this.GetIndexAndEntryForKey(key, out entry);
  327.  
  328.             if (this.SetEntry(key, value))
  329.             {
  330.                 version++;
  331.  
  332.                 // if prior entry existed for this key, fire the removed notifications
  333.                 if (index > -1)
  334.                 {
  335.                     this.FireEntryRemovedNotifications(entry, index);
  336.  
  337.                     // force the property change notifications to fire for the modified entry
  338.                     countCache--;
  339.                 }
  340.  
  341.                 // then fire the added notifications
  342.                 index = this.GetIndexAndEntryForKey(key, out entry);
  343.                 this.FireEntryAddedNotifications(entry, index);
  344.             }
  345.         }
  346.  
  347.         private void FireEntryAddedNotifications(DictionaryEntry entry, int index)
  348.         {
  349.             // fire the relevant PropertyChanged notifications
  350.             this.FirePropertyChangedNotifications();
  351.  
  352.             // fire CollectionChanged notification
  353.             if (index > -1)
  354.                 this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new KeyValuePair<TKey, TVal>((TKey)entry.Key, (TVal)entry.Value), index));
  355.             else
  356.                 this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  357.         }
  358.  
  359.         private void FireEntryRemovedNotifications(DictionaryEntry entry, int index)
  360.         {
  361.             // fire the relevant PropertyChanged notifications
  362.             this.FirePropertyChangedNotifications();
  363.  
  364.             // fire CollectionChanged notification
  365.             if (index > -1)
  366.                 this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new KeyValuePair<TKey, TVal>((TKey)entry.Key, (TVal)entry.Value), index));
  367.             else
  368.                 this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  369.         }
  370.  
  371.         private void FirePropertyChangedNotifications()
  372.         {
  373.             if (this.Count != countCache)
  374.             {
  375.                 countCache = Count;
  376.                 this.OnPropertyChanged("Count");
  377.                 this.OnPropertyChanged("Item[]");
  378.                 this.OnPropertyChanged("Keys");
  379.                 this.OnPropertyChanged("Values");
  380.             }
  381.         }
  382.  
  383.         private void FireResetNotifications()
  384.         {
  385.             // fire the relevant PropertyChanged notifications
  386.             this.FirePropertyChangedNotifications();
  387.  
  388.             // fire CollectionChanged notification
  389.             this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  390.         }
  391.  
  392.         #region ISerializable
  393.  
  394.         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  395.         {
  396.             info.AddValue("ItemCount", this.Count);
  397.             int itemIdx = 0;
  398.             foreach (KeyValuePair<TKey, TVal> kvp in this)
  399.             {
  400.                 info.AddValue(String.Format("Item{0}", itemIdx), kvp, typeof(KeyValuePair<TKey, TVal>));
  401.                 itemIdx++;
  402.             }
  403.         }
  404.  
  405.         #endregion
  406.  
  407.         #region IXmlSerializable
  408.  
  409.         void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
  410.         {
  411.             foreach (KeyValuePair<TKey, TVal> kvp in this)
  412.             {
  413.                 writer.WriteStartElement(ItemNodeName);
  414.                 writer.WriteStartElement(KeyNodeName);
  415.                 KeySerializer.Serialize(writer, kvp.Key);
  416.                 writer.WriteEndElement();
  417.                 writer.WriteStartElement(ValueNodeName);
  418.                 ValueSerializer.Serialize(writer, kvp.Value);
  419.                 writer.WriteEndElement();
  420.                 writer.WriteEndElement();
  421.             }
  422.         }
  423.  
  424.         void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
  425.         {
  426.             if (reader.IsEmptyElement)
  427.             {
  428.                 return;
  429.             }
  430.  
  431.             // Move past container
  432.             if (!reader.Read())
  433.             {
  434.                 throw new XmlException("Error in Deserialization of Dictionary");
  435.             }
  436.  
  437.             while (reader.NodeType != XmlNodeType.EndElement)
  438.             {
  439.                 reader.ReadStartElement(ItemNodeName);
  440.                 reader.ReadStartElement(KeyNodeName);
  441.                 TKey key = (TKey)KeySerializer.Deserialize(reader);
  442.                 reader.ReadEndElement();
  443.                 reader.ReadStartElement(ValueNodeName);
  444.                 TVal value = (TVal)ValueSerializer.Deserialize(reader);
  445.                 reader.ReadEndElement();
  446.                 reader.ReadEndElement();
  447.                 this.Add(key, value);
  448.                 reader.MoveToContent();
  449.             }
  450.  
  451.             // Read End Element to close Read of containing node
  452.             reader.ReadEndElement();
  453.         }
  454.  
  455.         System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
  456.         {
  457.             return null;
  458.         }
  459.  
  460.         protected XmlSerializer ValueSerializer
  461.         {
  462.             get
  463.             {
  464.                 if (valueSerializer == null)
  465.                 {
  466.                     valueSerializer = new XmlSerializer(typeof(TVal));
  467.                 }
  468.                 return valueSerializer;
  469.             }
  470.         }
  471.  
  472.         private XmlSerializer KeySerializer
  473.         {
  474.             get
  475.             {
  476.                 if (keySerializer == null)
  477.                 {
  478.                     keySerializer = new XmlSerializer(typeof(TKey));
  479.                 }
  480.                 return keySerializer;
  481.             }
  482.         }
  483.  
  484.         #endregion
  485.  
  486.         #region INotifyCollectionChanged
  487.  
  488.         public event NotifyCollectionChangedEventHandler CollectionChanged;
  489.  
  490.         protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
  491.         {
  492.             if (this.CollectionChanged != null)
  493.                 this.CollectionChanged(this, args);
  494.         }
  495.  
  496.         #endregion
  497.  
  498.         #region INotifyPropertyChanged
  499.  
  500.         public event PropertyChangedEventHandler PropertyChanged;
  501.  
  502.         protected virtual void OnPropertyChanged(string name)
  503.         {
  504.             if (this.PropertyChanged != null)
  505.                 this.PropertyChanged(this, new PropertyChangedEventArgs(name));
  506.         }
  507.  
  508.         #endregion
  509.  
  510.         protected class KeyedDictionaryEntryCollection<Tk> : KeyedCollection<Tk, DictionaryEntry>
  511.         {
  512.             public KeyedDictionaryEntryCollection() : base() { }
  513.  
  514.             public KeyedDictionaryEntryCollection(IEqualityComparer<Tk> comparer) : base(comparer) { }
  515.  
  516.             protected override Tk GetKeyForItem(DictionaryEntry entry)
  517.             {
  518.                 return (Tk)entry.Key;
  519.             }
  520.         }
  521.  
  522.         [Serializable, StructLayout(LayoutKind.Sequential)]
  523.         public struct Enumerator<Tk, TValue> : IEnumerator<KeyValuePair<Tk, TValue>>, IDisposable, IDictionaryEnumerator, IEnumerator
  524.         {
  525.             private ObservableDictionary<Tk, TValue> _dictionary;
  526.             private int _version;
  527.             private int _index;
  528.             private KeyValuePair<Tk, TValue> _current;
  529.             private bool _isDictionaryEntryEnumerator;
  530.  
  531.             internal Enumerator(ObservableDictionary<Tk, TValue> dictionary, bool isDictionaryEntryEnumerator)
  532.             {
  533.                 _dictionary = dictionary;
  534.                 _version = dictionary.version;
  535.                 _index = -1;
  536.                 _isDictionaryEntryEnumerator = isDictionaryEntryEnumerator;
  537.                 _current = new KeyValuePair<Tk, TValue>();
  538.             }
  539.  
  540.             public KeyValuePair<Tk, TValue> Current
  541.             {
  542.                 get
  543.                 {
  544.                     ValidateCurrent();
  545.                     return _current;
  546.                 }
  547.             }
  548.  
  549.             public void Dispose()
  550.             {
  551.             }
  552.  
  553.             public bool MoveNext()
  554.             {
  555.                 ValidateVersion();
  556.                 _index++;
  557.                 if (_index < _dictionary.keyedEntryCollection.Count)
  558.                 {
  559.                     _current = new KeyValuePair<Tk, TValue>((Tk)_dictionary.keyedEntryCollection[_index].Key, (TValue)_dictionary.keyedEntryCollection[_index].Value);
  560.                     return true;
  561.                 }
  562.                 _index = -2;
  563.                 _current = new KeyValuePair<Tk, TValue>();
  564.                 return false;
  565.             }
  566.  
  567.             private void ValidateCurrent()
  568.             {
  569.                 if (_index == -1)
  570.                 {
  571.                     throw new InvalidOperationException("The enumerator has not been started.");
  572.                 }
  573.                 else if (_index == -2)
  574.                 {
  575.                     throw new InvalidOperationException("The enumerator has reached the end of the collection.");
  576.                 }
  577.             }
  578.  
  579.             private void ValidateVersion()
  580.             {
  581.                 if (_version != _dictionary.version)
  582.                 {
  583.                     throw new InvalidOperationException("The enumerator is not valid because the dictionary changed.");
  584.                 }
  585.             }
  586.  
  587.             object IEnumerator.Current
  588.             {
  589.                 get
  590.                 {
  591.                     ValidateCurrent();
  592.                     if (_isDictionaryEntryEnumerator)
  593.                     {
  594.                         return new DictionaryEntry(_current.Key, _current.Value);
  595.                     }
  596.                     return new KeyValuePair<Tk, TValue>(_current.Key, _current.Value);
  597.                 }
  598.             }
  599.  
  600.             void IEnumerator.Reset()
  601.             {
  602.                 ValidateVersion();
  603.                 _index = -1;
  604.                 _current = new KeyValuePair<Tk, TValue>();
  605.             }
  606.  
  607.             DictionaryEntry IDictionaryEnumerator.Entry
  608.             {
  609.                 get
  610.                 {
  611.                     ValidateCurrent();
  612.                     return new DictionaryEntry(_current.Key, _current.Value);
  613.                 }
  614.             }
  615.  
  616.             object IDictionaryEnumerator.Key
  617.             {
  618.                 get
  619.                 {
  620.                     ValidateCurrent();
  621.                     return _current.Key;
  622.                 }
  623.             }
  624.  
  625.             object IDictionaryEnumerator.Value
  626.             {
  627.                 get
  628.                 {
  629.                     ValidateCurrent();
  630.                     return _current.Value;
  631.                 }
  632.             }
  633.         }
  634.     }
  635. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement