All files
[pithos-ms-client] / trunk / Libraries / ParallelExtensionsExtras / CoordinationDataStructures / ObservableConcurrentDictionary.cs
1 //--------------------------------------------------------------------------
2 // 
3 //  Copyright (c) Microsoft Corporation.  All rights reserved. 
4 // 
5 //  File: ObservableConcurrentDictionary.cs
6 //
7 //--------------------------------------------------------------------------
8
9 using System.Collections.Generic;
10 using System.Collections.Specialized;
11 using System.ComponentModel;
12 using System.Threading;
13 using System.Diagnostics;
14
15 namespace System.Collections.Concurrent
16 {
17     /// <summary>
18     /// Provides a thread-safe dictionary for use with data binding.
19     /// </summary>
20     /// <typeparam name="TKey">Specifies the type of the keys in this collection.</typeparam>
21     /// <typeparam name="TValue">Specifies the type of the values in this collection.</typeparam>
22     [DebuggerDisplay("Count={Count}")]
23     public class ObservableConcurrentDictionary<TKey, TValue> :
24         ICollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>,
25         INotifyCollectionChanged, INotifyPropertyChanged
26     {
27         private readonly SynchronizationContext _context;
28         private readonly ConcurrentDictionary<TKey, TValue> _dictionary;
29
30         /// <summary>
31         /// Initializes an instance of the ObservableConcurrentDictionary class.
32         /// </summary>
33         public ObservableConcurrentDictionary()
34         {
35             _context = AsyncOperationManager.SynchronizationContext;
36             _dictionary = new ConcurrentDictionary<TKey, TValue>();
37         }
38
39         /// <summary>Event raised when the collection changes.</summary>
40         public event NotifyCollectionChangedEventHandler CollectionChanged;
41         /// <summary>Event raised when a property on the collection changes.</summary>
42         public event PropertyChangedEventHandler PropertyChanged;
43
44         /// <summary>
45         /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
46         /// </summary>
47         private void NotifyObserversOfChange()
48         {
49             var collectionHandler = CollectionChanged;
50             var propertyHandler = PropertyChanged;
51             if (collectionHandler != null || propertyHandler != null)
52             {
53                 _context.Post(s =>
54                 {
55                     if (collectionHandler != null)
56                     {
57                         collectionHandler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
58                     }
59                     if (propertyHandler != null)
60                     {
61                         propertyHandler(this, new PropertyChangedEventArgs("Count"));
62                         propertyHandler(this, new PropertyChangedEventArgs("Keys"));
63                         propertyHandler(this, new PropertyChangedEventArgs("Values"));
64                     }
65                 }, null);
66             }
67         }
68
69         /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
70         /// <param name="item">The item to be added.</param>
71         /// <returns>Whether the add was successful.</returns>
72         private bool TryAddWithNotification(KeyValuePair<TKey, TValue> item)
73         {
74             return TryAddWithNotification(item.Key, item.Value);
75         }
76
77         /// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
78         /// <param name="key">The key of the item to be added.</param>
79         /// <param name="value">The value of the item to be added.</param>
80         /// <returns>Whether the add was successful.</returns>
81         private bool TryAddWithNotification(TKey key, TValue value)
82         {
83             bool result = _dictionary.TryAdd(key, value);
84             if (result) NotifyObserversOfChange();
85             return result;
86         }
87
88         /// <summary>Attempts to remove an item from the dictionary, notifying observers of any changes.</summary>
89         /// <param name="key">The key of the item to be removed.</param>
90         /// <param name="value">The value of the item removed.</param>
91         /// <returns>Whether the removal was successful.</returns>
92         private bool TryRemoveWithNotification(TKey key, out TValue value)
93         {
94             bool result = _dictionary.TryRemove(key, out value);
95             if (result) NotifyObserversOfChange();
96             return result;
97         }
98
99         /// <summary>Attempts to add or update an item in the dictionary, notifying observers of any changes.</summary>
100         /// <param name="key">The key of the item to be updated.</param>
101         /// <param name="value">The new value to set for the item.</param>
102         /// <returns>Whether the update was successful.</returns>
103         private void UpdateWithNotification(TKey key, TValue value)
104         {
105             _dictionary[key] = value;
106             NotifyObserversOfChange();
107         }
108
109         #region ICollection<KeyValuePair<TKey,TValue>> Members
110         void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
111         {
112             TryAddWithNotification(item);
113         }
114
115         void ICollection<KeyValuePair<TKey, TValue>>.Clear()
116         {
117             ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Clear();
118             NotifyObserversOfChange();
119         }
120
121         bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
122         {
123             return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Contains(item);
124         }
125
126         void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
127         {
128             ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);
129         }
130
131         int ICollection<KeyValuePair<TKey, TValue>>.Count
132         {
133             get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Count; }
134         }
135
136         bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
137         {
138             get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly; }
139         }
140
141         bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
142         {
143             TValue temp;
144             return TryRemoveWithNotification(item.Key, out temp);
145         }
146         #endregion
147
148         #region IEnumerable<KeyValuePair<TKey,TValue>> Members
149         IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
150         {
151             return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
152         }
153
154         IEnumerator IEnumerable.GetEnumerator()
155         {
156             return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
157         }
158         #endregion
159
160         #region IDictionary<TKey,TValue> Members
161         public void Add(TKey key, TValue value)
162         {
163             TryAddWithNotification(key, value);
164         }
165
166         public bool ContainsKey(TKey key)
167         {
168             return _dictionary.ContainsKey(key);
169         }
170
171         public ICollection<TKey> Keys
172         {
173             get { return _dictionary.Keys; }
174         }
175
176         public bool Remove(TKey key)
177         {
178             TValue temp;
179             return TryRemoveWithNotification(key, out temp);
180         }
181
182         public bool TryGetValue(TKey key, out TValue value)
183         {
184             return _dictionary.TryGetValue(key, out value);
185         }
186
187         public ICollection<TValue> Values
188         {
189             get { return _dictionary.Values; }
190         }
191
192         public TValue this[TKey key]
193         {
194             get { return _dictionary[key]; }
195             set { UpdateWithNotification(key, value); }
196         }
197         #endregion
198     }
199 }