2009-07-08 14 views
25

Estoy tratando de crear una Clase de Diccionario Observable para WPF DataBinding en C#. encontré un buen ejemplo de Andy aquí: Two Way Data Binding With a Dictionary in WPFClase de Diccionario Observable General para DataBinding/WPF C#

De acuerdo con eso, traté de cambiar el código a continuación:

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

}

Por desgracia, todavía no sé cómo pasar objetos del diccionario "generales" ... ¿alguna idea?

¡Gracias!

Saludos

+0

podría darle un poco más de detalle en cuanto a lo que está tratando de hacer? ¿Puede mostrar un ejemplo del código que le gustaría poder escribir cuando dice "pasar un diccionario general ..." – JMarsch

+0

Tengo diferentes diccionarios para, por ejemplo, un código postal y una ciudad. Lo que estoy tratando de hacer es: - Enlazar los datos (el modelo/el diccionario) a mi WPF ItemsControl, para que el usuario pueda, p. cambie la ciudad del código postal y el modelo se actualiza automáticamente. Desafortunadamente solo OneWay-Binding es posible con el diccionario "normal", porque necesitaría INotifyPropertyChanged. - Cree un ObservableDictionary, que implementa INotifyPropertyChanged y también contiene un diccionario –

+0

La solución está allí: http://stackoverflow.com/questions/5663395/net-observabledictionary –

Respuesta

35

Si realmente quiere hacer una ObservableDictionary, me gustaría sugerir la creación de una clase que implementa tanto IDictionary y INotifyCollectionChanged. Siempre puede usar un Dictionary internamente para implementar los métodos de IDictionary para que no tenga que volver a implementarlo usted mismo.

Dado que tiene un conocimiento completo de cuándo cambia el Dictionary interno, puede utilizar ese conocimiento para implementar INotifyCollectionChanged.

+2

¿Alguna posibilidad de que pueda elaborar con el código? estarás ayudando a muchos noobs (como yo) a rastrear stackOverflow para obtener respuestas - 23k vistas ya. chrs – BKSpurgeon

+2

@BKSpurgeon Ya hay un ejemplo disponible: http://blogs.microsoft.co.il/shimmy/2010/12/26/observabledictionarylttkey-tvaluegt-c/ (a través de http://stackoverflow.com/questions/5663395/net-observabledictionary, vinculado en los comentarios de la pregunta). – Andy

0

No se puede escribir algo que haga que el diccionario de otra persona, y mucho menos IDictionary, sea observable sin usar alguna forma de reflexión. El problema es que el Diccionario puede ser una subclase con mutadores adicionales (por ejemplo, Ordenar, o Filtro, o lo que sea) que no invocan Agregar y Eliminar y omiten sus eventos como resultado.

Creo que existen marcos de generación de código que te permiten hacer cosas como esta, pero no estoy familiarizado con ellas.

19
+0

Parece que no puedo implementar INotifyCollectionChanged. Dice falta de ensamblaje. Importé todos los ensamblajes en la parte superior de la publicación (C#) y tengo .NET 3.5, pero no puedo encontrarlo. ¿Algunas ideas? – Joshua

+0

Importó los espacios de nombres, pero quizás falten referencias a ensamblajes cruciales. Asegúrese de que el ensamblado System.dll se haga referencia en su proyecto. Vea el ejemplo [aquí] (http://i.stack.imgur.com/4kLAB.png). – Shimmy

6

Para propósitos históricos y poner a las personas en la ruta "actual" ... Es yo Es importante saber que Microsoft ahora resuelve este requisito en su plantilla de la "Página básica" de Windows Store en Visual Studio 2012. Para poder dar soporte a la LayoutAwarePage, generan una clase privada de ObservableDictionary.

Sin embargo, implementan una nueva interfaz IObservableMap en lugar de IDictionary directamente. Esta interfaz agrega un evento MapChanged y MapChangedEventHandler, definidos en el espacio de nombres Windows.Foundation.Collections.

El siguiente fragmento es simplemente la clase de los ObservableDictionary LayoutAwarePage.cs generados en la carpeta "común" de su proyecto:

/// <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 más detallado del nuevo espacio de nombres Windows.Foundation.Collections muestra una carga de nuevas interfaces definidas, pero solo una clase PropertySet implementada. En realidad, esto parece ser un buen observableDiccionario en sí mismo. Pero debe haber una razón por la cual la EM todavía genere un ObservableDictionary privado.Por lo tanto, se requiere un examen más detallado para identificar los pros y los contras.

En resumen, el PropertySet o su propio ObservableDictionary basado en IObservableMap deben resolver los requisitos inmediatos para los proyectos "actuales" de Windows 8 y Phone 8. Sin embargo, para los marcos antiguos (WPF 4 y Phone 7.5) todavía hay más trabajo por hacer.