gpt4 book ai didi

c# - ObservableCollection 中的项目在绑定(bind)到 WPF 列表框时不更新

转载 作者:行者123 更新时间:2023-12-05 05:21:43 25 4
gpt4 key购买 nike

我想通过将 String 绑定(bind)到 ListBoxItemsSource 属性来操作 ObservableCollection > 并将项目模板设置为 TextBox

我的问题是,当我在 ListBox 包含的 TextBox 项目中编辑它们时,ObservableCollection 中的项目没有得到更新。我做错了什么?

最小工作示例的 XAML 是

    <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 的评论所示,解决方案是数据绑定(bind)不能更改它所绑定(bind)到的对象本身,只能更改该对象的属性。

因此,我必须创建一个包装器类,其属性将成为我要操作的字符串。代码隐藏现在是

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 只需修改一次

<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。

更新:我觉得定义一个包装器只是为了操纵对象感到不舒服,而且在列表中编辑对象本身而不是它的一个属性的问题对我来说似乎是普遍的,所以我试图找到另一种解决方案。我宁愿 Hook ItemTemplateTextBoxLostFocus 事件。

在这种情况下,问题是从刚刚失去焦点的模板化 TextBox 中找到 ListBox 中的索引。我无法使用 ListBoxSelectedIndex 属性,因为在 LostFocus 触发时,它已经设置为不同的 ListBoxItem

我进行了大量搜索,并在此处找到了 Dennis Troller 的答案:WPF ListBoxItems with DataTemplates - How do I reference the CLR Object bound to ListBoxItem from within the DataTemplate?诀窍是让 TextBoxDataContext 失去焦点,并使用 ListBoxItemContainerGenerator 首先识别容器(使用 ItemContainerGenerator.ContainerFromItem)然后获取列表中的索引(使用 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 如下(我单向绑定(bind)了文本):

<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>

关于c# - ObservableCollection<string> 中的项目在绑定(bind)到 WPF 列表框时不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42459063/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com