2009-07-08 11 views
25

Sto provando a creare una classe del dizionario osservabile per WPF DataBinding in C#. ho trovato un bel esempio di Andy qui: Two Way Data Binding With a Dictionary in WPFClasse dizionario osservabile generale per DataBinding/WPF C#

Secondo tale, ho cercato di modificare il codice di seguito:

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

}

Purtroppo io ancora non so come passare Oggetti "generali" del dizionario .. qualche idea?

Grazie!

Acclamazioni

+0

Potrebbe dare un po 'più in dettaglio su ciò che si sta cercando di fare? Puoi mostrare un esempio del codice che vorresti essere in grado di scrivere quando dici "passa un dizionario generale ..." – JMarsch

+0

Ho dizionari diversi per es. un codice postale e una città. Quello che sto cercando di fare è: - Associare i dati (Modello/dizionario) al mio oggetto ItemControl, in modo che l'utente possa ad es. cambia la città del codice postale e il modello viene aggiornato automaticamente. Sfortunatamente solo OneWay-Binding è possibile con il dizionario "normale", perché avrei bisogno di INotifyPropertyChanged. - Crea un ObservableDictionary, che implementa INotifyPropertyChanged e contiene anche un dizionario –

+0

La soluzione è lì: http://stackoverflow.com/questions/5663395/net-observabledictionary –

risposta

35

Se davvero si vuole fare un ObservableDictionary, io suggerirei di creare una classe che implementa sia IDictionary e INotifyCollectionChanged. Puoi sempre usare internamente un Dictionary per implementare i metodi di IDictionary in modo da non doverlo reimplementare da solo.

Dal momento che si dispone di una conoscenza completa delle modifiche interne di Dictionary, è possibile utilizzare tale conoscenza per implementare INotifyCollectionChanged.

+2

ogni possibilità che potresti elaborare con il codice? aiuterete molti noobs (come me) a trascinare StackOverflow per le risposte - 23k visualizzazioni già. chrs – BKSpurgeon

+2

@BKSpurgeon Esiste un esempio già disponibile: http://blogs.microsoft.co.il/shimmy/2010/12/26/observabledictionarylttkey-tvaluegt-c/ (tramite http://stackoverflow.com/questions/5663395/net-observabledictionary, collegato nei commenti della domanda). – Andy

0

Non è possibile scrivere qualcosa che renderà il dizionario di qualcun altro, per non parlare di IDictionary, osservabile senza utilizzare una qualche forma di riflessione. Il problema è che il dizionario può essere una sottoclasse con mutatori aggiuntivi (ad esempio, Ordina, o Filtro, o qualsiasi altra cosa) che non invocano Aggiungi e rimuovi e ignorano i tuoi eventi come risultato.

Credo che esistano strutture di generazione di codice che ti consentano di fare cose del genere ma non mi sono familiari.

19
+0

Non riesco a implementare INotifyCollectionChanged. Dice il montaggio mancante. Ho importato tutti gli assembly nella parte superiore del tuo post (C#), e ho .NET 3.5, ma non riesco a trovarlo. Qualche idea? – Joshua

+0

Hai importato gli spazi dei nomi, ma forse mancano riferimenti agli assiemi cruciali. Assicurarsi che l'assembly System.dll sia referenziato nel progetto. Vedi esempio [qui] (http://i.stack.imgur.com/4kLAB.png). – Shimmy

6

Per scopi storici e per mettere le persone sul "corrente" percorso ... È io È importante sapere che Microsoft ora risolve questo requisito nel modello "Pagina di base" di Windows Store in Visual Studio 2012. Per supportare la pagina LayoutAware, viene generata una classe ObservableDictionary privata.

Tuttavia implementano direttamente una nuova interfaccia IObservableMap anziché IDictionary. Questa interfaccia aggiunge un evento MapChanged e MapChangedEventHandler, definiti nello spazio dei nomi Windows.Foundation.Collections.

Il frammento di seguito è solo la classe ObservableDictionary dalle LayoutAwarePage.cs generati nella cartella "Comune" del progetto:

/// <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 ulteriore esame del nuovo spazio dei nomi Windows.Foundation.Collections mostra un carico di nuove interfacce definite, ma solo una classe PropertySet implementata. In realtà questo sembra un buon ObservableDictionary stesso. Ma deve esserci una ragione per cui MS genera ancora un ObservableDictionary privato.Quindi è necessario un ulteriore esame per identificare i pro e i contro.

In breve, il PropertySet o il proprio ObservableDictionary basato su IObservableMap dovrebbe risolvere i requisiti immediati per i progetti "attuali" di Windows 8 e Phone 8. Tuttavia per i vecchi framework (WPF 4 e Phone 7.5) c'è ancora molto lavoro da fare.