C# 绑定到列表<;字符串>;不更新相应的字符串

C# 绑定到列表<;字符串>;不更新相应的字符串,c#,wpf,data-binding,C#,Wpf,Data Binding,我有以下XAML代码: <Window x:Class="WpfApplication1.MainWindow" x:Name="window" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schem

我有以下XAML代码:

<Window x:Class="WpfApplication1.MainWindow"
        x:Name="window"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="327" Width="213" 
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>          
        <ListBox ItemsSource="{Binding Strings}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Path=., UpdateSourceTrigger=PropertyChanged}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <TextBox Grid.Row="1" Text="{Binding TheString}" />
        <Button Click="ButtonBase_OnClick" Grid.Row="2">Check strings</Button>
    </Grid>
</Window>

检查字符串
很简单。下面是我的代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }

    public List<string> Strings { get; } = new List<string> { "Hello world1", "Hello world2", "Hello world3" };

    public string TheString { get; set; } = "Helloo";

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(string.Join("\n", this.Strings) + $"\n\n{this.TheString}");
    }
}
公共部分类主窗口:窗口
{
公共主窗口()
{
this.InitializeComponent();
}
公共列表字符串{get;}=新列表{“Hello world1”、“Hello world2”、“Hello world3”};
公共字符串字符串{get;set;}=“Helloo”;
private void按钮base_OnClick(对象发送方,RoutedEventTarget e)
{
Show(string.Join(“\n”,this.Strings)+$”\n\n{this.TheString});
}
}
问题是,如果我从UI更新
Strings
列表中的字符串值,当我单击按钮时,它永远不会更新。我做错了什么?
我已将
列表框
项资源
正确绑定到
字符串
列表。我还在
项目模板
中正确绑定了
文本框

变化

 public List<string> Strings { get; } = new List<string> { "Hello world1", "Hello world2", "Hello world3" };
公共列表字符串{get;}=新列表{“Hello world1”、“Hello world2”、“Hello world3”};

公共列表字符串{get;set;}=新列表{“Hello world1”、“Hello world2”、“Hello world3”};

如果没有setter,即使绑定了该属性,也无法更新该属性。

我认为您的问题在于绑定的列表类型。尝试改用ObservableCollection

public ObservableCollection<string> Strings { get; } = new ObservableCollection<string> { "Hello world1", "Hello world2", "Hello world3" };
public observetecollection Strings{get;}=new observetecollection{“Hello world1”、“Hello world2”、“Hello world3”};

也许这会奏效。

您遇到的问题是关于
字符串的不变性。更改
文本框中的文本时,创建新的
字符串
,旧字符串不会更改。因此,列表始终包含相同的值

要更新它,您应该将其包装在实现INotifyPropertyChanged的类中,并在setter中触发PropertyChanged事件。此外,还应更改对此新属性的绑定。例如:

public class Wrapper : INotifyPropertyChanged
{
    private string _value;
    public string Value
    {
        get { return _value; }
        set
        {
            _value = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
        }
    }

    //INotifyPropertyChanged implementation...
}
你的名单应该是:

public List<MyClass> Strings { get; } = new List<MyClass>
{
    new Wrapper { Value = "Hello world1" },
    new Wrapper { Value = "Hello world2" },
    new Wrapper { Value = "Hello world3" }
};
<TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}" />
公共列表字符串{get;}=新列表
{
新包装器{Value=“Hello world1”},
新包装器{Value=“Hello world2”},
新包装器{Value=“Hello world3”}
};
其约束力应为:

public List<MyClass> Strings { get; } = new List<MyClass>
{
    new Wrapper { Value = "Hello world1" },
    new Wrapper { Value = "Hello world2" },
    new Wrapper { Value = "Hello world3" }
};
<TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}" />

这里有两个问题

首先,您的UI永远不会知道是否用新列表替换列表,因为如果列表被重新实例化,它将不会收到通知

其次,对列表中项目的任何更改,无论是替换值还是添加/删除值,都不会引起注意,因为列表不会引发更改通知

您的最佳解决方案是@qqw2和@Lorek给出的答案的组合

将所有数据从窗口类中移出,并将其放入实现INotifyPropertyChanged的另一个类中。将字符串集合更改为ObservableCollection。 并且还可以在任何属性的setter中引发PropertyChanged事件,尽管这对于ObservableCollection可能不是必需的,只要您只在构造函数中实例化,并且只在需要删除所有条目时调用clear

public class DataViewModel : INotifyPropertyChanged
{
    private string _theString;

    public DataViewModel()
    {
        Strings = new ObservableCollection<string>();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<string> Strings { get; set; }

    public string TheString
    {
        get
        {
            return _theString;
        }
        set
        {
            _theString = value;
            NotifyPropertyChanged(nameof(TheString));
        }
    }

    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}
并添加一个新属性:

public DataViewModel ViewModel { get; set; }
并将窗口标记上的DataContext绑定更改为指向ViewModel属性

其他信息:

在视图模型上,添加以下方法

public void AddString() 
{
    Strings.Add(TheString) ;
    TheString  = string.Empty;
} 

然后从按钮点击处理程序调用它

是的,我也面临着这个问题!它似乎与字符串类型的不可变行为有关。我还写了一篇关于解决方案的文章


希望有帮助:)

A reach但将mode=twoway添加到TextBox默认的TextBox模式是twoway,但是您的列表没有setter。如果没有Setter,我认为您的集合将是只读的,因此您无法更新它。@Bearcat9425,我不是更新集合本身,而是更新它的成员,这也可以通过getter-only属性完成。您是否尝试在网格上不使用DataContext?如果集合是只读的,那么它的成员也是只读的。试一试推杆;在那个里,看看它是否适合你们。字符串是RW。列表字符串未更新-仅更新项目。仍然值得一试。我实际上是在更新字符串,而不是更改集合。我试了一下,但还是不行。对不起。如果VS现在在前面,我就不在前面,否则我会先测试它。这里有一个理论…当你的文本属性改变时,它们会设置一个新的字符串值,这意味着它们会创建一个新的字符串实例。绑定指向该新字符串,该字符串不是原始列表的成员。该新字符串仅由TextBox的绑定引用。似乎只有将“.”对象绑定到UI容器,然后将子UI元素绑定到该对象上的属性时,集合绑定的简写形式才有用。对于超级简单的情况,它失败了。即使没有INotifyPropertyChanged,它也能工作。但我觉得很奇怪,因为它仍然在做同样的事情,字符串仍然是不可变的,只是包装在另一个类中。我们仍然在更改字符串本身,现在是对这个类的整个引用。它是否与绑定的编写方式有关<代码>{Binding Path=.}
?如果从代码更新字符串并希望文本框显示更新的值,则需要使用
INotfiyPropertyChanged
部分。如果你愿意,可以省略它。对于绑定部分,如果更改TextBox中的值,则始终会创建一个新字符串。但是,要更新列表,必须删除旧值并添加新值,这不是绑定的工作方式。当您有一个包装器类时,它只是将您的字符串设置为新值并丢弃旧值(您不能使用
列表