2009-07-08 4 views
25

J'essaie de créer une classe de dictionnaire observable pour WPF DataBinding en C#. J'ai trouvé un bel exemple d'Andy ici: Two Way Data Binding With a Dictionary in WPFClasse de dictionnaire général observable pour DataBinding/WPF C#

D'après cela, j'ai essayé de changer le code suivant:

class ObservableDictionary : ViewModelBase 
{ 
    public ObservableDictionary(Dictionary<TKey, TValue> dictionary) 
    { 
     _data = dictionary; 
    } 

    private Dictionary<TKey, TValue> _data; 

    public Dictionary<TKey, TValue> Data 
    { 
     get { return this._data; } 
    } 

    private KeyValuePair<TKey, TValue>? _selectedKey = null; 
    public KeyValuePair<TKey, TValue>? SelectedKey 
    { 
     get { return _selectedKey; } 
     set 
     { 
      _selectedKey = value; 
      RaisePropertyChanged("SelectedKey"); 
      RaisePropertyChanged("SelectedValue"); 
     } 
    } 

    public TValue SelectedValue 
    { 
     get 
     { 
      return _data[SelectedKey.Value.Key]; 
     } 
     set 
     { 
      _data[SelectedKey.Value.Key] = value; 
      RaisePropertyChanged("SelectedValue"); 
     } 
    } 
} 

}

Malheureusement, je ne sais pas encore comment passer Objets du dictionnaire "général" .. des idées?

Merci!

Vive

+0

Pourriez-vous donner un peu plus de détails à ce que vous essayez de faire? Pouvez-vous montrer un exemple du code que vous aimeriez pouvoir écrire quand vous dites "passer un dictionnaire général ..." – JMarsch

+0

J'ai différents dictionnaires pour, par exemple, un code postal et une ville. Ce que j'essaie de faire est: - Lier les données (Modèle/le dictionnaire) à mon WPF ItemsControl, de sorte que l'utilisateur peut par exemple. changez la ville du code postal et le modèle est automatiquement mis à jour. Malheureusement, OneWay-Binding est seulement possible avec le dictionnaire "normal", car j'ai besoin de INotifyPropertyChanged. - Créer un ObservableDictionary, qui implémente INotifyPropertyChanged et contient également un dictionnaire –

+0

La solution est là: http://stackoverflow.com/questions/5663395/net-observabledictionary –

Répondre

35

Si vous voulez vraiment faire une ObservableDictionary, je vous suggère la création d'une classe qui implémente les deux IDictionary et INotifyCollectionChanged. Vous pouvez toujours utiliser un Dictionary en interne pour implémenter les méthodes de IDictionary afin que vous n'ayez pas à le réimplémenter vous-même.

Étant donné que vous avez une connaissance complète du moment où les modifications internes Dictionary, vous pouvez utiliser ces connaissances pour implémenter INotifyCollectionChanged.

+2

quelle chance pourriez-vous élaborer avec du code? vous aiderez beaucoup de noobs (comme moi) chalutage stackOverflow pour les réponses - 23k vues déjà. chrs – BKSpurgeon

+2

@BKSpurgeon Il existe un exemple déjà disponible: http://blogs.microsoft.co.il/shimmy/2010/12/26/observabledictionarylttkey-tvaluegt-c/ (via http://stackoverflow.com/questions/5663395/net-observabledictionary, lié dans les commentaires de la question). – Andy

0

Vous ne pouvez pas écrire quelque chose qui rendra le Dictionnaire de quelqu'un d'autre, et encore moins IDictionary, observable sans utiliser de forme de réflexion. Le problème est que le dictionnaire peut être une sous-classe avec des mutateurs supplémentaires (par exemple, Trier, ou Filtre, ou autre) qui n'invoquent pas Ajouter et Supprimer et contournent vos événements en conséquence. Je pense qu'il existe des cadres de génération de code qui permettent de faire des choses comme ça, mais je ne les connais pas.

19
+0

Je n'arrive pas à implémenter INotifyCollectionChanged. Il dit assemblage manquant. J'ai importé tous les assemblages en haut de votre message (C#), et j'ai .NET 3.5, mais il ne peut pas le trouver. Des idées? – Joshua

+0

Vous avez importé les espaces de noms, mais il manque peut-être des références aux assemblages cruciaux. Assurez-vous que l'assembly System.dll est référencé dans votre projet. Voir l'exemple [ici] (http://i.stack.imgur.com/4kLAB.png). – Shimmy

6

À des fins historiques et de mettre les gens sur le "courant" chemin ... C'est i Il est important de savoir que Microsoft répond maintenant à cette exigence dans leur modèle "Basic Page" de Windows Store dans Visual Studio 2012. Afin de prendre en charge LayoutAwarePage, ils génèrent une classe ObservableDictionary privée. Cependant, ils implémentent une nouvelle interface IObservableMap plutôt qu'IDictionary directement. Cette interface ajoute un événement MapChanged et MapChangedEventHandler, définis dans l'espace de noms Windows.Foundation.Collections.

L'extrait ci-dessous est juste la classe ObservableDictionary des LayoutAwarePage.cs générés dans le dossier « Common » de votre projet:

/// <summary> 
    /// Implementation of IObservableMap that supports reentrancy for use as a default view 
    /// model. 
    /// </summary> 
    private class ObservableDictionary<K, V> : IObservableMap<K, V> 
    { 
     private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs<K> 
     { 
      public ObservableDictionaryChangedEventArgs(CollectionChange change, K key) 
      { 
       CollectionChange = change; 
       Key = key; 
      } 

      public CollectionChange CollectionChange { get; private set; } 
      public K Key { get; private set; } 
     } 

     private Dictionary<K, V> _dictionary = new Dictionary<K, V>(); 
     public event MapChangedEventHandler<K, V> MapChanged; 

     private void InvokeMapChanged(CollectionChange change, K key) 
     { 
      var eventHandler = MapChanged; 
      if (eventHandler != null) 
      { 
       eventHandler(this, new ObservableDictionaryChangedEventArgs(change, key)); 
      } 
     } 

     public void Add(K key, V value) 
     { 
      _dictionary.Add(key, value); 
      InvokeMapChanged(CollectionChange.ItemInserted, key); 
     } 

     public void Add(KeyValuePair<K, V> item) 
     { 
      Add(item.Key, item.Value); 
     } 

     public bool Remove(K key) 
     { 
      if (_dictionary.Remove(key)) 
      { 
       InvokeMapChanged(CollectionChange.ItemRemoved, key); 
       return true; 
      } 
      return false; 
     } 

     public bool Remove(KeyValuePair<K, V> item) 
     { 
      V currentValue; 
      if (_dictionary.TryGetValue(item.Key, out currentValue) && 
       Object.Equals(item.Value, currentValue) && _dictionary.Remove(item.Key)) 
      { 
       InvokeMapChanged(CollectionChange.ItemRemoved, item.Key); 
       return true; 
      } 
      return false; 
     } 

     public V this[K key] 
     { 
      get 
      { 
       return _dictionary[key]; 
      } 
      set 
      { 
       _dictionary[key] = value; 
       InvokeMapChanged(CollectionChange.ItemChanged, key); 
      } 
     } 

     public void Clear() 
     { 
      var priorKeys = _dictionary.Keys.ToArray(); 
      _dictionary.Clear(); 
      foreach (var key in priorKeys) 
      { 
       InvokeMapChanged(CollectionChange.ItemRemoved, key); 
      } 
     } 

     public ICollection<K> Keys 
     { 
      get { return _dictionary.Keys; } 
     } 

     public bool ContainsKey(K key) 
     { 
      return _dictionary.ContainsKey(key); 
     } 

     public bool TryGetValue(K key, out V value) 
     { 
      return _dictionary.TryGetValue(key, out value); 
     } 

     public ICollection<V> Values 
     { 
      get { return _dictionary.Values; } 
     } 

     public bool Contains(KeyValuePair<K, V> item) 
     { 
      return _dictionary.Contains(item); 
     } 

     public int Count 
     { 
      get { return _dictionary.Count; } 
     } 

     public bool IsReadOnly 
     { 
      get { return false; } 
     } 

     public IEnumerator<KeyValuePair<K, V>> GetEnumerator() 
     { 
      return _dictionary.GetEnumerator(); 
     } 

     System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
     { 
      return _dictionary.GetEnumerator(); 
     } 

     public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex) 
     { 
      if (array == null) throw new ArgumentNullException("array"); 
      int arraySize = array.Length; 
      foreach (var pair in _dictionary) 
      { 
       if (arrayIndex >= arraySize) break; 
       array[arrayIndex++] = pair; 
      } 
     } 
    } 

Un examen plus approfondi du nouvel espace de noms Windows.Foundation.Collections montre une charge de nouvelles interfaces définies, mais une seule classe PropertySet implémentée. En fait, cela semble être un très bon observableDictionary lui-même. Mais il doit y avoir une raison pour que MS génère toujours un ObservableDictionary privé.Un examen plus approfondi est donc nécessaire ici pour identifier les avantages et les inconvénients. En bref, le PropertySet ou votre propre ObservableDictionary basé sur IObservableMap devrait résoudre les besoins immédiats des projets Windows 8 et Phone 8 "actuels". Cependant, pour les frameworks plus anciens (WPF 4 et Phone 7.5), il reste encore du travail à faire.

Questions connexes