c# класса Двусторонняя привязка данных с помощью словаря в WPF




wpf dictionary binding (4)

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

Сделал ключ только для чтения, поскольку ключ, вероятно, не должен изменяться. Для этого требуется, чтобы Prism работала - или вы можете реализовать собственную версию INotifyPropertyChanged вместо 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;
    }
}

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

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

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

Я нашел этот вопрос: Access codebehind variable в XAML, но по-прежнему не может скрыть разрыв.

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

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


Answer #1

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

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

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

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

Конечно, в этой реализации Key не используется, поэтому вы можете просто использовать 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>

Answer #2

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

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

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 в модели представления.

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


Answer #3

Вместо

Dictionary<string, int>

, у вас есть

Dictionary<string, IntWrapperClass>?

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





dictionary