2009-04-28 1 views
22

Я хотел бы связать Dictionary<string, int> с ListView в WPF. Я хотел бы сделать это так, чтобы Values в Dictionary обновился через механизм привязки данных. Я не хочу менять Keys только Values. Я также не забочусь о добавлении новых сопоставлений в Dictionary. Я просто хочу обновить существующие.Двусторонняя привязка данных с помощью словаря в WPF

Установка словаря как ItemsSourceListView не выполняет этого. Это не работает, потому что ListView использует Enumerator для доступа к содержимому Dictionary, и элементы этого перечисления неизменяемы KeyValuePair объектов.

Моя нынешняя линия расследования пытается использовать имущество Keys. Я назначаю это свойству ItemsSource моего ListView. Это позволяет мне отображать Keys, но я не знаю достаточно о механизме привязки WPF для доступа к Values в Dictionary.

Я нашел этот вопрос: Access codebehind variable in XAML, но все еще, похоже, не может преодолеть разрыв.

Вы знаете, как это сделать? У кого-то есть лучший подход?

Похоже, в крайнем случае, я мог бы построить пользовательский объект и вставить его в List, из которого я воссоздавать/обновить мой Dictionary, но это, кажется, как способ обойти встроенные привязки данных функциональных возможностей, а не эффективно использовать его.

+1

бы не лучше использовать что-то вроде ObservableDictionary с WPF? – Rauhotz

+0

Как вы собираетесь сделать это двумя способами? Отображаются ли значения в текстовом поле? – Ray

+0

@Ray У меня есть собственный редактор для моих значений. Если бы у меня был пример с TextBox, я уверен, что смогу удовлетворить его потребности. –

ответ

17

Я не думаю, что вы сможете делать то, что хотите, со словарем.

  1. Поскольку словарь не реализует INotifyPropertyChanged или INotifyCollectionChanged
  2. Потому что он не собирается, чтобы два способа связывания, как вы хотите.

Я не уверен, что он соответствует вашим требованиям точно, но я бы использовал ObservableCollection и пользовательский класс.

Я использую DataTemplate, чтобы показать, как сделать привязку значений двумя способами.

Конечно, в этом ключ реализации не используется, так что вы могли бы просто использовать ObservableCollection<int>

пользовательского класса

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

Набор 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> 
+1

На самом деле я меньше беспокоюсь о распространении изменений в словаре в ListView, чем о распространении изменений из ListView в словаре. Интерфейсы INotify выглядят так, как будто они в основном связаны с первым. –

+0

Да, это правильно. Однако я попытался правильно привязать словарь и не смог заставить его работать, как вы не могли. Увидит, есть ли у кого-нибудь предложения. – Ray

+0

MyCustomClass необходимо реализовать INotifyPropertyChanged – MikeT

6

Это немного взломанный, но я получил его на работу (предполагая, что я понимаю, чего вы хотите).

Я создал первый взгляд класс модели:

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

А затем в 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> 

Значение Data собственности привязанный к ItemsSource в ListBox. Как вы заявляете в вопросе, это приводит к использованию экземпляров KeyValuePair<int, string> в качестве данных позади ListBox. Я установил DisplayMemberPath на Key, так что значение ключа будет использоваться как отображаемое значение для каждого элемента в ListBox.

Как вы нашли, вы не можете просто использовать ValueKeyValuePair как данные для TextBox, так как это только для чтения. Вместо этого TextBox привязан к свойству модели представления, которое может получить и установить значение для текущего выбранного ключа (который обновляется путем привязки свойства SelectedItemListBox к другому свойству модели представления). Я должен был сделать это свойство нулевым (поскольку KeyValuePair - это структура), чтобы код мог обнаружить, когда нет выбора.

В моем тестовом приложении это, похоже, приводит к изменениям в TextBox, распространяющимся на Dictionary в модели представления.

Это более или менее то, что вы собираетесь использовать? Похоже, что это должно быть немного чище, но я не уверен, есть ли способ сделать это.

+0

моя ситуация немного отличается.Я хочу показать словаря items как несколько пар Label и Textbox. Есть ли у вас какие-либо идеи? – YukiSakura

+0

@YukiSakura Я бы начал с найденного словаря ItemsControl и установил для шаблона элемента привязку Label и TextBox к соответствующим свойствам. – Andy

+0

Я пробовал, но во время выполнения была ошибка, говорящая, что «не может использовать TwoWay для readonly KeyValuePair ...» – YukiSakura

0

Вместо

Dictionary<string, int> 

, вы можете иметь

Dictionary<string, IntWrapperClass>? 

Have IntWrapperClass реализации INotifyPropertyChanged. Затем вы можете иметь двустороннюю привязку Listview/Listbox к элементам в словаре.

0

Просто опубликуйте, что я получил из вышеперечисленного. Вы можете разместить нижеследующее значение в ObservableCollection<ObservableKvp<MyKeyObject, MyValueObject>>

Сделано ключ только для чтения, поскольку ключ, вероятно, не должен изменяться. Это необходимо Prism работать - или вы можете реализовать свою собственную версию INotifyPropertyChanged вместо

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; 
    } 
} 
Смежные вопросы