Last active
July 22, 2018 10:30
-
-
Save bezzad/09ca415d9ccb053395c60745578f6201 to your computer and use it in GitHub Desktop.
Provides a thread-safe dictionary for use with data binding.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Collections.Specialized; | |
using System.ComponentModel; | |
using System.Threading; | |
namespace System.Collections.Generic | |
{ | |
/// <summary> | |
/// Provides a thread-safe dictionary for use with data binding. | |
/// </summary> | |
/// <typeparam name="TKey">Specifies the type of the keys in this collection.</typeparam> | |
/// <typeparam name="TValue">Specifies the type of the values in this collection.</typeparam> | |
public class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, INotifyCollectionChanged | |
{ | |
private readonly SynchronizationContext _context; | |
private readonly ConcurrentDictionary<TKey, TValue> _dictionary; | |
/// <summary> | |
/// Initializes an instance of the ObservableConcurrentDictionary class. | |
/// </summary> | |
public ObservableConcurrentDictionary() | |
{ | |
_context = AsyncOperationManager.SynchronizationContext; | |
_dictionary = new ConcurrentDictionary<TKey, TValue>(); | |
} | |
/// <summary>Event raised when the collection changes.</summary> | |
public event NotifyCollectionChangedEventHandler CollectionChanged; | |
/// <summary> | |
/// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary. | |
/// </summary> | |
private void NotifyObserversOfChange(NotifyCollectionChangedEventArgs args) | |
{ | |
_context.Post(s => CollectionChanged?.Invoke(this, args), null); | |
} | |
/// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary> | |
/// <param name="item">The item to be added.</param> | |
/// <returns>Whether the add was successful.</returns> | |
private bool TryAddWithNotification(KeyValuePair<TKey, TValue> item) | |
{ | |
return TryAddWithNotification(item.Key, item.Value); | |
} | |
/// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary> | |
/// <param name="key">The key of the item to be added.</param> | |
/// <param name="value">The value of the item to be added.</param> | |
/// <returns>Whether the add was successful.</returns> | |
private bool TryAddWithNotification(TKey key, TValue value) | |
{ | |
var result = _dictionary.TryAdd(key, value); | |
if (result) NotifyObserversOfChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); | |
return result; | |
} | |
/// <summary>Attempts to remove an item from the dictionary, notifying observers of any changes.</summary> | |
/// <param name="key">The key of the item to be removed.</param> | |
/// <param name="value">The value of the item removed.</param> | |
/// <returns>Whether the removal was successful.</returns> | |
private bool TryRemoveWithNotification(TKey key, out TValue value) | |
{ | |
var result = _dictionary.TryRemove(key, out value); | |
if (result) NotifyObserversOfChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value)); | |
return result; | |
} | |
/// <summary>Attempts to add or update an item in the dictionary, notifying observers of any changes.</summary> | |
/// <param name="key">The key of the item to be updated.</param> | |
/// <param name="value">The new value to set for the item.</param> | |
/// <returns>Whether the update was successful.</returns> | |
private void UpdateWithNotification(TKey key, TValue value) | |
{ | |
_dictionary.TryGetValue(key, out var oldValue); | |
_dictionary[key] = value; | |
NotifyObserversOfChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); | |
} | |
#region ICollection<KeyValuePair<TKey,TValue>> Members | |
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) | |
{ | |
TryAddWithNotification(item); | |
} | |
void ICollection<KeyValuePair<TKey, TValue>>.Clear() | |
{ | |
var values = Values; | |
(_dictionary as ICollection<KeyValuePair<TKey, TValue>>).Clear(); | |
NotifyObserversOfChange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, values)); | |
} | |
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) | |
{ | |
return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Contains(item); | |
} | |
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) | |
{ | |
((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex); | |
} | |
int ICollection<KeyValuePair<TKey, TValue>>.Count => (_dictionary as ICollection<KeyValuePair<TKey, TValue>>).Count; | |
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly; | |
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) | |
{ | |
return TryRemoveWithNotification(item.Key, out _); | |
} | |
#endregion | |
#region IEnumerable<KeyValuePair<TKey,TValue>> Members | |
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() | |
{ | |
return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator(); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator(); | |
} | |
#endregion | |
#region IDictionary<TKey,TValue> Members | |
public void Add(TKey key, TValue value) | |
{ | |
TryAddWithNotification(key, value); | |
} | |
public bool ContainsKey(TKey key) | |
{ | |
return _dictionary.ContainsKey(key); | |
} | |
public ICollection<TKey> Keys => _dictionary.Keys; | |
public bool Remove(TKey key) | |
{ | |
return TryRemoveWithNotification(key, out _); | |
} | |
public bool TryGetValue(TKey key, out TValue value) | |
{ | |
return _dictionary.TryGetValue(key, out value); | |
} | |
public ICollection<TValue> Values => _dictionary.Values; | |
public TValue this[TKey key] | |
{ | |
get => _dictionary[key]; | |
set => UpdateWithNotification(key, value); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment