7

J'essaie de créer un ObservableConcurrentDictionary. Cet objet sera utilisé dans une application multithread et ses données sont utilisées pour remplir un contrôle via la propriété ItemsSource des contrôles.Comment créer une collection observable personnalisée à l'aide de ConcurrentDictionary, INotifyCollectionChanged, INotifyPropertyChanged

C'est la mise en œuvre, je suis venu avec:

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 
} 

Malheureusement, la solution ne fonctionne pas comme prévu - en fait, il ne fonctionne pas du tout. Des idées sur ce que je fais de mal ou de faire de meilleures solutions existent?

Veuillez noter que je ne peux pas utiliser ObservableCollection, par conséquent je dois écrire ma propre collection Observable.

EDIT: La version de travail est ci-dessous. J'espère que cela aide quelqu'un d'autre avec un problème similaire.

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; 
} 
+0

"cela ne fonctionne pas du tout" - n'est pas une description de problème valide. Dites-nous quelles erreurs obtenez-vous, quel code utilisez-vous pour le tester et pourquoi vous ne pouvez pas utiliser ObservableCollection? – Euphoric

Répondre

2

va rapidement grâce à votre code sans eplanation de votre côté, je ne peux que deviner. Je ne pense pas de paramètre Action sur NotifyCollectionChangedEventArgs est suffisant. Il existe également des propriétés NewItems, OldItems, qui indiquent à l'abonné quels éléments ont été modifiés.

Notez également que, bien que ce soient des collections, de nombreux composants WPF ne prennent en charge qu'une seule modification d'élément à la fois via DataBinding.

+0

Mes excuses pour la vague description. Hier a été une journée longue et frustrante. Tu m'as fait bouger dans la bonne direction. Tout fonctionne correctement maintenant. Merci! – c0D3l0g1c

11

Je n'ai pas réussi à faire fonctionner l'échantillon OP. A soulevé une charge de tronc d'exceptions de filetage croisé tout le temps, peu importe ce que j'ai essayé. Cependant, comme j'avais aussi besoin d'une collection thread-safe qui implémente les interfaces INotifyCollectionChanged et INotifyPropertyChanged, j'ai fait un tour d'adresse et trouvé une implémentation des mecs chez Microsoft eux-mêmes.

Téléchargez ce fichier http://code.msdn.microsoft.com/Samples-for-Parallel-b4b76364 et recherchez ObservableConcurrentDictionary.cs dans les archives.

Fonctionne comme un charme!

+1

Ce lien vient de changer ma vie. THX :) – Quanta

1

J'ai développé une version étroite d'un ObservableConcurrentDictionnary, s'il vous plaît commentaires/suggestions ...

... où TValue: Object {utilisez votre classe au lieu d'objets ...}

Qurlet

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; 
     } 

    } 
}