C# 使用 WPF 中的字典进行双向数据绑定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/800130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Two Way Data Binding With a Dictionary in WPF
提问by Waylon Flinn
I'd like to bind a Dictionary<string, int>
to a ListView
in WPF. I'd like to do this in such a way that the Values
in the Dictionary
get updated via the data binding mechanism. I don't want to change the Keys
just the Values
. I also don't care about adding new mappings to the Dictionary
. I just want to update existing ones.
我想将 a 绑定Dictionary<string, int>
到ListView
WPF 中的 a 。我希望做这样一种方式,Values
在Dictionary
得到通过数据绑定机制更新。我不想改变Keys
只是Values
. 我也不关心将新映射添加到Dictionary
. 我只想更新现有的。
Setting the Dictionary as the ItemsSource
of the ListView
doesn't accomplish this. It doesn't work because the ListView
uses the Enumerator to access the contents of the Dictionary
and the elements of that enumeration are immutable KeyValuePair
objects.
将 Dictionary 设置为ItemsSource
ofListView
并不能实现这一点。它不起作用,因为ListView
使用 Enumerator 访问 的内容Dictionary
并且该枚举的元素是不可变的KeyValuePair
对象。
My current line of investigation attempts to use the Keys
property. I assign this to the ItemsSource
property of my ListView
. This does allow me to display the Keys
but I don't know enough about WPF's databinding mechanism to access the Values
in the Dictionary
.
我目前的调查线试图使用该Keys
财产。我将此分配给ItemsSource
我的ListView
. 这并不让我来显示Keys
,但我不知道有足够的了解WPF的数据绑定机制来访问Values
的Dictionary
。
I found this question: Access codebehind variable in XAMLbut still can't seem to bridge the gap.
我发现了这个问题:在 XAML 中访问代码隐藏变量,但似乎仍然无法弥合差距。
Do any of you know how to make this approach work? Does anyone have a better approach?
你们中有人知道如何使这种方法起作用吗?有没有人有更好的方法?
It seems like, as a last resort, I could build a custom object and stick it in a List
from which I recreate/update my Dictionary
but this seems like a way to circumvent the built-in data binding functionality rather than effectively utilize it.
看起来,作为最后的手段,我可以构建一个自定义对象并将其粘贴在一个List
我重新创建/更新我的对象中,Dictionary
但这似乎是一种规避内置数据绑定功能而不是有效利用它的方法。
回答by Ray
I don't think you're going to be able to do what you'd like with a dictionary.
我不认为你能用字典做你想做的事。
- Because the Dictionary doesn't implement INotifyPropertyChanged or INotifyCollectionChanged
- Because it's not going to allow two way binding like you want.
- 因为字典没有实现 INotifyPropertyChanged 或 INotifyCollectionChanged
- 因为它不会像你想要的那样允许双向绑定。
I'm not sure if it fits your requirements exactly, but I would use an ObservableCollection
, and a custom class.
我不确定它是否完全符合您的要求,但我会使用ObservableCollection
, 和自定义类。
I'm using a DataTemplate, to show how to make the binding on the values two way.
我正在使用 DataTemplate,以展示如何以两种方式对值进行绑定。
Of course in this implementation Key isn't used, so you could just use an ObservableCollection<int>
当然在这个实现中没有使用 Key,所以你可以只使用一个 ObservableCollection<int>
Custom Class
自定义类
public class MyCustomClass
{
public string Key { get; set; }
public int Value { get; set; }
}
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
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>
回答by Andy
This is a little hacky, but I got it to work (assuming that I understand what you want).
这有点hacky,但我让它工作了(假设我明白你想要什么)。
I first created a view model class:
我首先创建了一个视图模型类:
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));
}
}
}
And then in the XAML:
然后在 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>
The value of the Data
property is bound to the ItemsSource
of the ListBox
. As you state in the question, this results in using instances of KeyValuePair<int, string>
as the data behind the ListBox
. I set the DisplayMemberPath
to Key
so that the value of the key will be used as the displayed value for each item in the ListBox
.
Data
属性的值绑定到ItemsSource
的ListBox
。正如您在问题中所述,这会导致使用 的实例KeyValuePair<int, string>
作为ListBox
. 我将 设置为DisplayMemberPath
,Key
以便键的值将用作ListBox
.
As you found, you can't just use the Value
of the KeyValuePair
as the data for the TextBox
, since that is read only. Instead, the TextBox
is bound to a property on the view model which can get and set the value for the currently selected key (which is updated by binding the SelectedItem
property of the ListBox
to another property on the view model). I had to make this property nullable (since KeyValuePair
is a struct), so that the code could detect when there is no selection.
正如您所发现的,您不能仅Value
将KeyValuePair
用作 的数据TextBox
,因为它是只读的。相反,TextBox
绑定到视图模型上的一个属性,该属性可以获取和设置当前选定键的值(通过将 的SelectedItem
属性绑定ListBox
到视图模型上的另一个属性来更新)。我必须使这个属性可以为空(因为它KeyValuePair
是一个结构体),以便代码可以检测到何时没有选择。
In my test app, this seems to result in edits to the TextBox
propagating to the Dictionary
in the view model.
在我的测试应用程序中,这似乎导致对TextBox
传播到Dictionary
视图模型中的进行编辑。
Is that more or less what you are going for? It seems like it should be a bit cleaner, but I'm not sure if there is any way to do so.
这或多或少是你想要的吗?看起来它应该更干净一些,但我不确定是否有任何方法可以这样做。
回答by Adarsha
Instead of
代替
Dictionary<string, int>
, can you have a
,你能有一个
Dictionary<string, IntWrapperClass>?
Have IntWrapperClass implement INotifyPropertyCHanged. Then you can have a Listview/Listbox two-way bind to items in the dictionary.
让 IntWrapperClass 实现 INotifyPropertyCHanged。然后你可以有一个 Listview/Listbox 双向绑定到字典中的项目。
回答by Anthony Nichols
Just posting what I derived from the above. You can place the below in a ObservableCollection<ObservableKvp<MyKeyObject, MyValueObject>>
只是发布我从上面得出的内容。您可以将以下内容放在ObservableCollection<ObservableKvp<MyKeyObject, MyValueObject>>
Made the Key read-only as they key probably shouldn't be changed. This needs Prism to work - or you can implement your own version of INotifyPropertyChanged
instead
将密钥设为只读,因为它们的密钥可能不应更改。这需要棱镜工作-或者你可以实现自己的版本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;
}
}
回答by elfo11
I managed to do that in my project using an observable dictionary implemented by dr.wpf hereand using as Value not a String but an object[]. In xaml i got something like:
我设法在我的项目中使用由 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>