Dot-Net
如何使用 ConcurrentDictionary、INotifyCollectionChanged、INotifyPropertyChanged 創建自定義可觀察集合
我正在嘗試創建一個 ObservableConcurrentDictionary。該對象將在多執行緒應用程序中使用,它的數據用於通過控制項的 ItemsSource 屬性填充控制項。
這是我提出的實現:
public sealed class ObservableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged { #region Constructors public ObservableConcurrentDictionary() : base() { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : base(collection) { } public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity) : base(concurrencyLevel, capacity) { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(collection, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, capacity, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, collection, comparer) { } #endregion #region Public Methods public new TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) { // Update value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace)); } // Else if key does not exist else { // Add value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); } // Returns the value return value; } public void Clear() { // Clear dictionary base.Clear(); // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) // Get value value = base.GetOrAdd(key, valueFactory); // Else if key does not exist else { // Add value and raise event value = base.GetOrAdd(key, valueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); } // Return value return value; } public new TValue GetOrAdd(TKey key, TValue value) { // If key exists if (base.ContainsKey(key)) // Get value base.GetOrAdd(key, value); // Else if key does not exist else { // Add value and raise event base.GetOrAdd(key, value); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); } // Return value return value; } public new bool TryAdd(TKey key, TValue value) { // Stores tryAdd bool tryAdd; // If added if (tryAdd = base.TryAdd(key, value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add)); // Return tryAdd return tryAdd; } public new bool TryRemove(TKey key, out TValue value) { // Stores tryRemove bool tryRemove; // If removed if (tryRemove = base.TryRemove(key, out value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove)); // Return tryAdd return tryRemove; } public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { // Stores tryUpdate bool tryUpdate; // If updated if (tryUpdate = base.TryUpdate(key, newValue, comparisonValue)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace)); // Return tryUpdate return tryUpdate; } #endregion #region Private Methods private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (CollectionChanged != null) CollectionChanged(this, e); } #endregion #region INotifyCollectionChanged Members public event NotifyCollectionChangedEventHandler CollectionChanged; #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion }不幸的是,該解決方案沒有按預期工作 - 事實上,它根本不起作用。關於我做錯了什麼或是否存在任何更好的解決方案的任何想法?
請注意我不能使用 ObservableCollection,因此我必須編寫自己的 Observable 集合。
編輯:工作版本如下。希望這可以幫助其他有類似問題的人。
public sealed class ObservableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged { public ObservableConcurrentDictionary() : base() { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : base(collection) { } public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity) : base(concurrencyLevel, capacity) { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(collection, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, capacity, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : base(concurrencyLevel, collection, comparer) { } public new TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) { // Update value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value)); } // Else if key does not exist else { // Add value and raise event value = base.AddOrUpdate(key, addValueFactory, updateValueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); } // Returns the value return value; } public new void Clear() { // Clear dictionary base.Clear(); // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) { // Stores the value TValue value; // If key exists if (base.ContainsKey(key)) // Get value value = base.GetOrAdd(key, valueFactory); // Else if key does not exist else { // Add value and raise event value = base.GetOrAdd(key, valueFactory); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); } // Return value return value; } public new TValue GetOrAdd(TKey key, TValue value) { // If key exists if (base.ContainsKey(key)) // Get value base.GetOrAdd(key, value); // Else if key does not exist else { // Add value and raise event base.GetOrAdd(key, value); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); } // Return value return value; } public new bool TryAdd(TKey key, TValue value) { // Stores tryAdd bool tryAdd; // If added if (tryAdd = base.TryAdd(key, value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); // Return tryAdd return tryAdd; } public new bool TryRemove(TKey key, out TValue value) { // Stores tryRemove bool tryRemove; // If removed if (tryRemove = base.TryRemove(key, out value)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value)); // Return tryAdd return tryRemove; } public new bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { // Stores tryUpdate bool tryUpdate; // If updated if (tryUpdate = base.TryUpdate(key, newValue, comparisonValue)) // Raise event OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue)); // Return tryUpdate return tryUpdate; } private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (CollectionChanged != null) CollectionChanged(this, e); } public event NotifyCollectionChangedEventHandler CollectionChanged; public event PropertyChangedEventHandler PropertyChanged; }
我只能猜測,在沒有任何解釋的情況下快速瀏覽您的程式碼。我認為在 NotifyCollectionChangedEventArgs 上設置 Action 是不夠的。還有
NewItems,OldItems屬性,告訴訂閱者哪些項目發生了變化。另請注意,雖然這些是集合,但許多 WPF 組件一次僅支持通過 DataBinding 更改單個項目。
我開發了 ObservableConcurrentDictionnary 的緊湊版本,請評論/建議…
…其中 TValue : Object {使用您的類而不是 Object…}
曲子
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; namespace Collections { public class ObservableConcurrentDictionary<TValue> : ConcurrentDictionary<Int32, TValue>, INotifyCollectionChanged, INotifyPropertyChanged where TValue : Object , new() { public event NotifyCollectionChangedEventHandler CollectionChanged; protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs changeAction) { var eh = CollectionChanged; if (eh == null) return; eh(this, changeAction); OnPropertyChanged(); } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged() { var eh = PropertyChanged; if (eh == null) return; // All properties : Keys, Values, Count, IsEmpty eh(this, new PropertyChangedEventArgs(null)); } #region Ctors public ObservableConcurrentDictionary() : base() { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<Int32, TValue>> collection) : base(collection) { } public ObservableConcurrentDictionary(IEqualityComparer<Int32> comparer) : base(comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity) : base(concurrencyLevel, capacity) { } public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<Int32, TValue>> collection, IEqualityComparer<Int32> comparer) : base(collection, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<Int32> comparer) : base(concurrencyLevel, capacity, comparer) { } public ObservableConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<Int32, TValue>> collection, IEqualityComparer<Int32> comparer) : base(concurrencyLevel, collection, comparer) { } #endregion public new void Clear() { // Clear dictionary base.Clear(); // Raise event OnCollectionChanged(changeAction: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public new TValue AddOrUpdate(Int32 key, Func<Int32, TValue> addValueFactory, Func<Int32, TValue, TValue> updateValueFactory) { bool isUpdated = false; TValue oldValue = default(TValue); TValue value = base.AddOrUpdate(key, addValueFactory, (k, v) => { isUpdated = true; oldValue = v; return updateValueFactory(k, v); }); if (isUpdated) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return value; } public new TValue AddOrUpdate(Int32 key, TValue addValue, Func<Int32, TValue, TValue> updateValueFactory) { bool isUpdated = false; TValue oldValue = default(TValue); TValue value = base.AddOrUpdate(key, addValue, (k, v) => { isUpdated = true; oldValue = v; return updateValueFactory(k, v); }); if (isUpdated) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return value; } public new TValue GetOrAdd(Int32 key, Func<Int32, TValue> addValueFactory) { bool isAdded = false; TValue value = base.GetOrAdd(key, k => { isAdded = true; return addValueFactory(k); }); if (isAdded) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return value; } public new TValue GetOrAdd(Int32 key, TValue value) { return GetOrAdd(key, k => value); } public new bool TryAdd(Int32 key, TValue value) { bool tryAdd = base.TryAdd(key, value); if (tryAdd) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value)); return tryAdd; } public new bool TryRemove(Int32 key, out TValue value) { // Stores tryRemove bool tryRemove = base.TryRemove(key, out value); // If removed raise event if (tryRemove) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value)); return tryRemove; } public new bool TryUpdate(Int32 key, TValue newValue, TValue comparisonValue) { // Stores tryUpdate bool tryUpdate = base.TryUpdate(key, newValue, comparisonValue); if (tryUpdate) OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newValue, comparisonValue)); return tryUpdate; } } }