ObservableCollection< string>中的项目绑定到WPF ListBox时不更新 [英] Items in ObservableCollection<string> do not update when bound to a WPF ListBox
问题描述
我想通过将string
的ObservableCollection
绑定到ListBox
的ItemsSource
属性并将项目模板设置为TextBox
来操作.
I should like to manipulate an ObservableCollection
of string
s by binding it to the ItemsSource
property of a ListBox
and setting the item template to a TextBox
.
我的问题是,当我在ListBox
所包含的TextBox
项目中对其进行编辑时,ObservableCollection
中的项目不会得到更新.我在做什么错了?
My problem is that the items in the ObservableCollection
do not get updated when I edit them in the TextBox
items that the ListBox
contains. What am I doing wrong?
最小工作示例的XAML是
The XAML of the minimum working example is
<Window x:Class="ListBoxUpdate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxUpdate" Height="300" Width="300"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Show items" Click="HandleButtonClick"/>
<TextBlock Grid.Row="1" x:Name="textBlock" />
</Grid>
<ListBox
Grid.Column="1"
ItemsSource="{Binding Strings, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding ., Mode=TwoWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
后面的相应代码是
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace ListBoxUpdate
{
public partial class Window1 : Window
{
public ObservableCollection<string> Strings { get; set; }
public Window1()
{
InitializeComponent();
Strings = new ObservableCollection<string>(new string[] { "one", "two", "three" });
this.DataContext = this;
}
void HandleButtonClick(object sender, RoutedEventArgs e)
{
string text = "";
for (int i = 0; i < Strings.Count; i++) {
text += Strings[i] + Environment.NewLine;
}
textBlock.Text = text;
}
}
}
非常感谢您的建议.
推荐答案
正如@BrandonKramer和@Peter Duniho的评论所示,解决方案是数据绑定不能更改绑定到的对象本身,而只能更改该对象的属性.对象.
As comments by @BrandonKramer and @Peter Duniho show, the solution is that data binding cannot change the object itself to which it is bound, only the properties of that object.
因此,我必须创建一个包装器类,该包装器的属性将成为我要操作的字符串.现在的代码是
Consequently, I have to create a wrapper class whose property will then be the string I want to manipulate. The code-behind is now
using System;
using System.Collections.ObjectModel;
using System.Windows;
namespace ListBoxUpdate
{
public partial class Window1 : Window
{
public ObservableCollection<StringWrapper> Strings { get; set; }
public class StringWrapper
{
public string Content { get; set; }
public StringWrapper(string content)
{
this.Content = content;
}
public static implicit operator StringWrapper(string content)
{
return new Window1.StringWrapper(content);
}
}
public Window1()
{
InitializeComponent();
this.Strings = new ObservableCollection<StringWrapper>(new StringWrapper[] { "one", "two", "three" });
this.DataContext = this;
}
void HandleButtonClick(object sender, RoutedEventArgs e)
{
string text = "";
for (int i = 0; i < Strings.Count; i++) {
text += Strings[i].Content + Environment.NewLine;
}
textBlock.Text = text;
}
}
}
并且XAML只需要在一点上进行修改
and the XAML has to be modified only at one point
<ListBox
Grid.Column="1"
ItemsSource="{Binding Strings, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Content, Mode=TwoWay}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
谢谢@BrandonKramer和@Peter Duniho.
Thank you, @BrandonKramer and @Peter Duniho.
更新:我为定义一个仅用于操作对象的包装感到不自在,而且在列表中编辑对象本身而不是其属性之一的问题在我看来似乎很普遍,因此我尝试寻找另一种解决方案.我宁愿决定挂接到ItemTemplate
中TextBox
的LostFocus
事件.
UPDATE: I felt uncomfortable with defining a wrapper just to manipulate objects, and the problem of editing an object itself in a list and not one of its properties seemed to me universal, so I tried to find another solution. I rather decided to hook to the LostFocus
event of the TextBox
in the ItemTemplate
.
在这种情况下,问题是从刚刚失去焦点的模板化TextBox
中找到ListBox
中的索引.我不能使用ListBox
的SelectedIndex
属性,因为LostFocus
触发时,它已经设置为其他ListBoxItem
.
In this case, the problem is finding the index in the ListBox
from the templated TextBox
that is just losing focus. I cannot use the SelectedIndex
property of the ListBox
as at the time when a LostFocus
fires, it is already set to a different ListBoxItem
.
我搜索了很多,并在这里找到了丹尼斯·托勒(Dennis Troller)的答案:
I searched quite a bit and have found Dennis Troller’s answer here: WPF ListBoxItems with DataTemplates - How do I reference the CLR Object bound to ListBoxItem from within the DataTemplate? The trick is to get the DataContext
of the TextBox
losing focus and use the ItemContainerGenerator
of the ListBox
to first identify the container (with ItemContainerGenerator.ContainerFromItem
) then obtain the index in the list (with ItemContainerGenerator.IndexFromContainer
).
隐藏的代码现在是
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace ListBoxUpdate
{
public partial class Window1 : Window
{
public ObservableCollection<string> Strings { get; set; }
public Window1()
{
InitializeComponent();
this.Strings = new ObservableCollection<string>(new string[] { "one", "two", "three" });
this.DataContext = this;
}
void HandleButtonClick(object sender, RoutedEventArgs e)
{
string text = "";
for (int i = 0; i < Strings.Count; i++) {
text += Strings[i] + Environment.NewLine;
}
textBlock.Text = text;
}
void HandleTextBoxLostFocus(object sender, RoutedEventArgs e)
{
// https://stackoverflow.com/questions/765984/wpf-listboxitems-with-datatemplates-how-do-i-reference-the-clr-object-bound-to?rq=1, @Dennis Troller's answer.
int index;
object item;
DependencyObject container;
TextBox textBox = sender as TextBox;
if (textBox == null) return;
item = textBox.DataContext;
container = listBox.ItemContainerGenerator.ContainerFromItem(item);
if (container != null) {
index = listBox.ItemContainerGenerator.IndexFromContainer(container);
if (textBox.Text != Strings[index]) {
Strings[index] = textBox.Text;
}
}
}
}
}
使用完整的XAML,如下所示(我单向绑定了文本):
with the full XAML as follows (I made the binding to the text one-way):
<Window x:Class="ListBoxUpdate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxUpdate" Height="300" Width="300"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Show items" Click="HandleButtonClick"/>
<TextBlock Grid.Row="1" x:Name="textBlock" />
</Grid>
<ListBox
x:Name="listBox"
Grid.Column="1"
ItemsSource="{Binding Strings}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox
Text="{Binding ., Mode=OneWay}"
LostFocus="HandleTextBoxLostFocus"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
这篇关于ObservableCollection< string>中的项目绑定到WPF ListBox时不更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!