我有以下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
。
答案 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)
希望它会有所帮助:)