Двухстороннее связывание данных со словарем в WPF - PullRequest
23 голосов
/ 29 апреля 2009

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

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

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

Я нашел этот вопрос: Доступ к переменной codebehind в XAML , но все еще не могу устранить разрыв.

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

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

Ответы [ 4 ]

19 голосов
/ 29 апреля 2009

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

  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>
6 голосов
/ 29 апреля 2009

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

Сначала я создал класс модели представления:

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.

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

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

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

1 голос
/ 10 марта 2017

Просто публикую то, что я получил из вышеизложенного. Вы можете разместить ниже в 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;
    }
}
0 голосов
/ 18 мая 2012

вместо

Dictionary<string, int>

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

Dictionary<string, IntWrapperClass>?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...