代码之家  ›  专栏  ›  技术社区  ›  Waylon Flinn

wpf中带字典的双向数据绑定

  •  23
  • Waylon Flinn  · 技术社区  · 16 年前

    我想绑一个 Dictionary<string, int> 到A ListView 在WPF中。我想这样做 Values Dictionary 通过数据绑定机制进行更新。我不想改变 Keys 只是 价值观 . 我也不关心添加新的映射到 词典 . 我只想更新现有的。

    将字典设置为 ItemsSource 列表视图 不能做到这一点。它不起作用,因为 列表视图 使用枚举器访问 词典 枚举的元素是不可变的 KeyValuePair 物体。

    我目前的调查试图利用 钥匙 财产。我把这个分配给 小精灵 我的财产 列表视图 . 这确实允许我显示 钥匙 但我对wpf的数据绑定机制了解不够,无法访问 价值观 词典 .

    我发现这个问题: Access codebehind variable in XAML 但似乎仍无法弥合差距。

    你们中有人知道如何使这种方法奏效吗? 有没有更好的方法?

    似乎,作为最后的手段,我可以构建一个自定义对象并将其粘贴到 List 从中重新创建/更新 词典 但这似乎是一种绕过内置数据绑定功能的方法,而不是有效地利用它。

    4 回复  |  直到 8 年前
        1
  •  20
  •   Ray    16 年前

    我认为你不能用字典做你想做的事。

    1. 因为字典没有实现inotifyPropertyChanged或inotifyCollectionChanged
    2. 因为它不会像你想要的那样允许双向绑定。

    我不确定它是否符合你的要求,但我会用 ObservableCollection ,以及自定义类。

    我正在使用一个数据模板,来演示如何对值进行双向绑定。

    当然,在这个实现中没有使用密钥,所以您可以使用 ObservableCollection<int>

    自定义类

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

    设置项源

    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>
    
        2
  •  6
  •   Andy    16 年前

    这有点老套,但我已经开始工作了(假设我明白你想要什么)。

    我首先创建了一个视图模型类:

    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> 作为背后的数据 列表框 . 我设定 DisplayMemberPath Key 以便键的值将用作 列表框 .

    正如你发现的,你不能仅仅使用 Value KeyValuePair 作为 TextBox ,因为它是只读的。相反, 文本框 绑定到视图模型上的属性,该属性可以获取并设置当前选定的键的值(通过绑定 SelectedItem 财产 列表框 到视图模型上的另一个属性)。我不得不使这个属性为空(因为 键值空气 是一个结构),以便代码可以在没有选择时检测到。

    在我的测试应用程序中,这似乎会导致对 文本框 传播到 Dictionary 在视图模型中。

    这或多或少是你想要的吗?看起来应该干净一点,但我不确定有没有办法。

        3
  •  1
  •   Anthony Nichols    8 年前

    只是发布我从上面得到的。你可以把下面的 ObservableCollection<ObservableKvp<MyKeyObject, MyValueObject>>

    使密钥为只读,因为它们的密钥可能不应更改。这需要棱镜来工作,或者你可以实现你自己的版本。 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;
        }
    }
    
        4
  •  0
  •   Adarsha    13 年前

    而不是

    Dictionary<string, int>
    

    你能拥有一个

    Dictionary<string, IntWrapperClass>?
    

    已更改IntWrapperClass实现InotifyPropertyChanged。然后,可以对字典中的条目进行Listview /ListBox双向绑定。