绑定到List <string>不更新相应的字符串

时间:2016-03-17 21:11:50

标签: 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://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}");
    }
}

问题是如果我从UI更新Strings列表中的字符串值,当我单击按钮时它永远不会更新。我做错了什么?
我正确地将Listbox&#39; ItemsSource绑定到Strings列表。我也正确绑定TextBox中的ItemTemplate

5 个答案:

答案 0 :(得分:2)

您遇到的问题是callParent的不变性。当您更改string中的文字时,您会创建一个新的TextBox,旧的string不会更改。因此List始终包含相同的值。

要更新它,您应将其包装在实现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}" />

答案 1 :(得分:1)

这里有两个问题。

首先,您的用户界面永远不会知道您是否用新列表替换列表,因为如果List&lt;&gt;重新实例化。

其次,无论是替换值还是添加/删除值,列表中项目的任何更改都将被忽略,因为列表不会引发更改通知。

您最好的解决方案是@ qqww2和@Lorek给出的答案的组合。

将所有数据从Window类移开,并将其放在另一个实现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));
        }
    }
}

在Window类中,更改构造函数:

public MainWindow()
{
    this.InitializeComponent();
    this.ViewModel = new DataViewModel();
}

并添加一个新属性:

public DataViewModel ViewModel { get; set; }

并更改Window标记上的DataContext绑定以指向ViewModel属性。

其他信息:

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

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

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

答案 2 :(得分:0)

变化

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

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

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

答案 3 :(得分:0)

我认为你的问题是你绑定的列表类型。请尝试使用ObservableCollection。

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

也许这会奏效。

答案 4 :(得分:-1)

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

希望它会有所帮助:)