c# wpf data-binding xaml dictionary

c# - Enlace de datos bidireccional con un diccionario en WPF



data-binding xaml (4)

En lugar de

Dictionary<string, int>

, puedes tener un

Dictionary<string, IntWrapperClass>?

Haga que IntWrapperClass implemente INotifyPropertyCHanged. Entonces puede hacer que Listview / Listbox se vinculen bidireccionalmente a los elementos en el diccionario.

Me gustaría vincular un Dictionary<string, int> a un ListView en WPF. Me gustaría hacer esto de tal manera que los Values en el Dictionary se actualicen a través del mecanismo de enlace de datos. No quiero cambiar las Keys solo los Values . Tampoco me importa agregar nuevas asignaciones al Dictionary . Solo quiero actualizar los existentes.

Establecer el diccionario como ItemsSource de ListView no lo logra. No funciona porque ListView usa el Enumerador para acceder a los contenidos del Dictionary y los elementos de esa enumeración son objetos inmutables KeyValuePair .

Mi línea actual de investigación intenta usar la propiedad Keys . ItemsSource esto a la propiedad ItemsSource de mi ListView . Esto me permite mostrar las Keys pero no sé lo suficiente sobre el mecanismo de enlace de datos de WPF para acceder a los Values en el Dictionary .

Encontré esta pregunta: El código de acceso detrás de la variable en XAML pero todavía no parece cerrar la brecha.

¿Alguno de ustedes sabe cómo hacer que este enfoque funcione? ¿Alguien tiene un mejor enfoque?

Parece que, como último recurso, podría construir un objeto personalizado y pegarlo en una List desde la que recrear / actualizar mi Dictionary pero esto parece una forma de eludir la funcionalidad integrada de enlace de datos en lugar de utilizarla de manera efectiva.


Esto es un poco raro, pero lo hice funcionar (asumiendo que entiendo lo que quieres).

Primero creé una clase de modelo de vista:

class ViewModel : INotifyPropertyChanged { public ViewModel() { this.data.Add(1, "One"); this.data.Add(2, "Two"); this.data.Add(3, "Three"); } Dictionary<int, string> data = new Dictionary<int, string>(); public IDictionary<int, string> Data { get { return this.data; } } private KeyValuePair<int, string>? selectedKey = null; public KeyValuePair<int, string>? SelectedKey { get { return this.selectedKey; } set { this.selectedKey = value; this.OnPropertyChanged("SelectedKey"); this.OnPropertyChanged("SelectedValue"); } } public string SelectedValue { get { if(null == this.SelectedKey) { return string.Empty; } return this.data[this.SelectedKey.Value.Key]; } set { this.data[this.SelectedKey.Value.Key] = value; this.OnPropertyChanged("SelectedValue"); } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propName) { var eh = this.PropertyChanged; if(null != eh) { eh(this, new PropertyChangedEventArgs(propName)); } } }

Y luego en el XAML:

<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <ListBox x:Name="ItemsListBox" Grid.Row="0" ItemsSource="{Binding Path=Data}" DisplayMemberPath="Key" SelectedItem="{Binding Path=SelectedKey}"> </ListBox> <TextBox Grid.Row="1" Text="{Binding Path=SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> </Grid> </Window>

El valor de la propiedad Data está vinculado a ItemsSource de ListBox . Como indica en la pregunta, esto resulta en el uso de instancias de KeyValuePair<int, string> como los datos detrás de ListBox . DisplayMemberPath en Key para que el valor de la clave se use como el valor mostrado para cada elemento en el ListBox .

Como descubrió, no puede usar el Value de KeyValuePair como datos para TextBox , ya que solo es de lectura. En cambio, TextBox está vinculado a una propiedad en el modelo de vista que puede obtener y establecer el valor de la clave seleccionada actualmente (que se actualiza al vincular la propiedad SelectedItem del ListBox a otra propiedad en el modelo de vista). Tuve que hacer esta propiedad KeyValuePair (ya que KeyValuePair es una estructura), por lo que el código podría detectar cuando no hay selección.

En mi aplicación de prueba, esto parece dar como resultado modificaciones al TextBox propagan al Dictionary en el modelo de vista.

¿Es eso más o menos lo que estás buscando? Parece que debería ser un poco más limpio, pero no estoy seguro de si hay alguna manera de hacerlo.


No creo que puedas hacer lo que quieras con un diccionario.

  1. Porque el diccionario no implementa INotifyPropertyChanged o INotifyCollectionChanged
  2. Porque no va a permitir el enlace bidireccional como desees.

No estoy seguro de si se ajusta exactamente a sus requisitos, pero usaría un ObservableCollection y una clase personalizada.

Estoy usando DataTemplate, para mostrar cómo hacer que el enlace en los valores sea bidireccional.

Por supuesto, en esta implementación no se utiliza la clave, por lo que podría usar un ObservableCollection<int>

Clase personalizada

public class MyCustomClass { public string Key { get; set; } public int Value { get; set; } }

Establecer ItemsSource

ObservableCollection<MyCustomClass> dict = new ObservableCollection<MyCustomClass>(); dict.Add(new MyCustomClass{Key = "test", Value = 1}); dict.Add(new MyCustomClass{ Key = "test2", Value = 2 }); listView.ItemsSource = dict;

XAML

<Window.Resources> <DataTemplate x:Key="ValueTemplate"> <TextBox Text="{Binding Value}" /> </DataTemplate> </Window.Resources> <ListView Name="listView"> <ListView.View> <GridView> <GridViewColumn CellTemplate="{StaticResource ValueTemplate}"/> </GridView> </ListView.View> </ListView>


Solo publicando lo que derivé de lo anterior. Puede colocar lo siguiente en un ObservableCollection<ObservableKvp<MyKeyObject, MyValueObject>>

Hizo que la clave sea de solo lectura, ya que probablemente la clave no debería cambiarse. Esto necesita que Prism funcione, o puede implementar su propia versión de INotifyPropertyChanged en INotifyPropertyChanged lugar

public class ObservableKvp<K, V> : BindableBase { public K Key { get; } private V _value; public V Value { get { return _value; } set { SetProperty(ref _value, value); } } public ObservableKvp(K key, V value) { Key = key; Value = value; } }