[英]Two Way Data Binding With a Dictionary in WPF
我想将Dictionary<string, int>
绑定到 WPF 中的ListView
。 我想通过数据绑定机制来更新Dictionary
中的Values
。 我不想只更改Keys
Values
。 我也不关心向Dictionary
添加新映射。 我只想更新现有的。
将 Dictionary 设置为ListView
的ItemsSource
并不能实现这一点。 它不起作用,因为ListView
使用 Enumerator 来访问Dictionary
的内容,并且该枚举的元素是不可变的KeyValuePair
对象。
我目前的调查尝试使用Keys
属性。 我将此分配给我的ListView
的ItemsSource
属性。 这确实允许我显示Keys
但我对 WPF 的数据绑定机制了解得不够多,无法访问Dictionary
的Values
。
我发现了这个问题: 在 XAML 中访问代码隐藏变量,但似乎仍然无法弥合差距。
你们中有人知道如何使这种方法起作用吗? 有没有人有更好的方法?
看起来,作为最后的手段,我可以构建一个自定义对象并将其粘贴在一个List
,从中我重新创建/更新我的Dictionary
但这似乎是一种绕过内置数据绑定功能而不是有效利用它的方法。
我不认为你能用字典做你想做的事。
我不确定它是否完全符合您的要求,但我会使用ObservableCollection
和自定义类。
我正在使用 DataTemplate,以展示如何以两种方式对值进行绑定。
当然在这个实现中没有使用 Key,所以你可以只使用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>
这有点hacky,但我让它工作了(假设我明白你想要什么)。
我首先创建了一个视图模型类:
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
属性的值绑定到ListBox
的ItemsSource
。 正如您在问题中所述,这会导致使用KeyValuePair<int, string>
实例作为ListBox
背后的数据。 我将DisplayMemberPath
设置为Key
以便键的值将用作ListBox
每个项目的显示值。
正如您所发现的,您不能只使用KeyValuePair
的Value
作为TextBox
的数据,因为它是只读的。 相反, TextBox
绑定到视图模型上的一个属性,该属性可以获取和设置当前选定键的值(通过将ListBox
的SelectedItem
属性绑定到视图模型上的另一个属性来更新)。 我必须将此属性设为可空(因为KeyValuePair
是一个结构体),以便代码可以检测到何时没有选择。
在我的测试应用程序中,这似乎导致对TextBox
编辑传播到视图模型中的Dictionary
。
这或多或少是你想要的吗? 看起来它应该更干净一些,但我不确定是否有任何方法可以这样做。
只是发布我从上面得出的内容。 您可以将以下内容放入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;
}
}
代替
Dictionary<string, int>
,你能有一个
Dictionary<string, IntWrapperClass>?
让 IntWrapperClass 实现 INotifyPropertyCHanged。 然后你可以有一个 Listview/Listbox 双向绑定到字典中的项目。
我设法在我的项目中使用由 dr.wpf 在这里实现的可观察字典并使用作为值而不是字符串而是对象 []。 在 xaml 中,我得到了类似的信息:
<ListView ItemsSource="{Binding myObservableDictionary}" >
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Key}"/>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Value[0], Mode=TwoWay}" AllowDrop="True" Drop="TextBox_Drop"></TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.