当绑定到WPF ListBox时,ObservableCollection <string>中的项不会更新

时间:2017-02-25 17:12:37

标签: c# wpf string data-binding listbox

我想通过将ObservableCollection绑定到string的{​​{1}}属性并将项目模板设置为ItemsSource来操纵ListBox TextBoxObservableCollection }。

我的问题是TextBox中的项目在ListBox包含的 <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> 项目中进行编辑时不会得到更新。我做错了什么?

最小工作示例的XAML是

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

虽然相应的代码隐藏是

dplyr::slice(df, match(c(3, 5, 7), x))

非常感谢您的建议。

1 个答案:

答案 0 :(得分:1)

正如@BrandonKramer和@Peter Duniho所展示的那样,解决方案是数据绑定不能改变它所绑定的对象本身,只能改变该对象的属性。

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

<ListBox
    Grid.Column="1"
    ItemsSource="{Binding Strings, Mode=TwoWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Content, Mode=TwoWay}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

并且必须仅在一个点修改XAML

LostFocus

谢谢@BrandonKramer和@Peter Duniho。

更新:我觉得定义一个包装器只是为了操纵对象感到不舒服,而且在列表中编辑对象本身而不是其中一个属性的问题在我看来很普遍,所以我试图找到另一个解决方案。我宁愿决定挂钩TextBoxItemTemplate的{​​{1}}事件。

在这种情况下,问题是从模板化ListBox中找到TextBox中的索引,该索引正在失去焦点。我无法使用SelectedIndex的{​​{1}}属性,因为当ListBox触发时,它已设置为不同的LostFocus

我搜索了很多,并在这里找到了Dennis Troller的答案:WPF ListBoxItems with DataTemplates - How do I reference the CLR Object bound to ListBoxItem from within the DataTemplate?诀窍是让ListBoxItem失去焦点并DataContext使用TextBox ItemContainerGenerator首先标识容器(使用ListBox)然后获取列表中的索引(使用ItemContainerGenerator.ContainerFromItem)。

代码隐藏现在是

ItemContainerGenerator.IndexFromContainer

完整的XAML如下(我单向绑定了文本):

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