ObservableCollection<string> 中的项目绑定到 WPF ListBox 时不更新 [英] Items in ObservableCollection<string> do not update when bound to a WPF ListBox

查看:39
本文介绍了ObservableCollection<string> 中的项目绑定到 WPF ListBox 时不更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想通过将 stringObservableCollection 绑定到 ListBoxItemsSource 属性来操作它> 并将项目模板设置为 TextBox.

I should like to manipulate an ObservableCollection of strings 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 中挂钩 TextBoxLostFocus 事件.

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 中的索引.我不能使用 ListBoxSelectedIndex 属性,因为在触发 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 的答案:WPF ListBoxItems with DataTemplates - 如何从 DataTemplate 中引用绑定到 ListBoxItem 的 CLR 对象? 诀窍是获取 DataContextTextBox的code>失去焦点,使用ListBoxItemContainerGenerator先识别容器(用ItemContainerGenerator.ContainerFromItem)然后获取列表中的索引(使用ItemContainerGenerator.IndexFromContainer).

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&lt;string&gt; 中的项目绑定到 WPF ListBox 时不更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆