为什么 List<T> 属性 的通知不起作用
Why notification for List<T> property doesn't work
为什么 List<T>
属性 的上升 INotifypPropertyChanged
不起作用?
考虑这个 MCVE:
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string property = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public class TextWrapper
{
public string Text { get; set; }
public override string ToString() => Text;
}
public class ViewModel : NotifyPropertyChanged
{
public List<string> List { get; } = new List<string>();
public TextWrapper Text { get; } = new TextWrapper();
public void AddToList(string text)
{
List.Add(text);
OnPropertyChanged(nameof(List));
}
public void ChangeText(string text)
{
Text.Text = text;
OnPropertyChanged(nameof(Text));
}
}
public partial class MainWindow : Window
{
readonly ViewModel _vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
}
}
xaml:
<TextBlock Text="{Binding Text}" />
<ListBox ItemsSource="{Binding List}" />
调用 _vm.ChangeText(...)
将正确更新 TextBlock
,而调用 _vm.AddToList(...)
不会更新 ListBox
(它将保持为空)。为什么?
请注意:我知道 ObservableCollection<T>
并且我知道两种可能的解决方法(将 setter 添加到 List
并将其设置为例如 null
先然后再返回或更改 DataContext
/ItemsSource
)。我只是好奇屋檐下是什么让 List<T>
比 TextWrapper
更特别。
当 WPF 绑定处理 PropertyChanged
事件时,它不会更新其目标 属性 除非它产生的有效值实际发生了变化。
因此,除非 List
属性 值实际发生变化(添加元素时不会发生变化),否则调用
OnPropertyChanged(nameof(List));
没有效果。
替换
public List<string> List { get; } = new List<string>();
来自
public ObservableCollection<string> List { get; } = new ObservableCollection<string>();
并像这样编写 AddToList
方法:
public void AddToList(string text)
{
List.Add(text);
}
对于您的 TextWrapper class:由于您直接绑定到 TextWrapper 实例,因此 Binding 会调用其覆盖的 ToString()
方法,因此每当 TextWrapper 的 Text
属性 已更改。
为什么 List<T>
属性 的上升 INotifypPropertyChanged
不起作用?
考虑这个 MCVE:
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string property = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public class TextWrapper
{
public string Text { get; set; }
public override string ToString() => Text;
}
public class ViewModel : NotifyPropertyChanged
{
public List<string> List { get; } = new List<string>();
public TextWrapper Text { get; } = new TextWrapper();
public void AddToList(string text)
{
List.Add(text);
OnPropertyChanged(nameof(List));
}
public void ChangeText(string text)
{
Text.Text = text;
OnPropertyChanged(nameof(Text));
}
}
public partial class MainWindow : Window
{
readonly ViewModel _vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
}
}
xaml:
<TextBlock Text="{Binding Text}" />
<ListBox ItemsSource="{Binding List}" />
调用 _vm.ChangeText(...)
将正确更新 TextBlock
,而调用 _vm.AddToList(...)
不会更新 ListBox
(它将保持为空)。为什么?
请注意:我知道 ObservableCollection<T>
并且我知道两种可能的解决方法(将 setter 添加到 List
并将其设置为例如 null
先然后再返回或更改 DataContext
/ItemsSource
)。我只是好奇屋檐下是什么让 List<T>
比 TextWrapper
更特别。
当 WPF 绑定处理 PropertyChanged
事件时,它不会更新其目标 属性 除非它产生的有效值实际发生了变化。
因此,除非 List
属性 值实际发生变化(添加元素时不会发生变化),否则调用
OnPropertyChanged(nameof(List));
没有效果。
替换
public List<string> List { get; } = new List<string>();
来自
public ObservableCollection<string> List { get; } = new ObservableCollection<string>();
并像这样编写 AddToList
方法:
public void AddToList(string text)
{
List.Add(text);
}
对于您的 TextWrapper class:由于您直接绑定到 TextWrapper 实例,因此 Binding 会调用其覆盖的 ToString()
方法,因此每当 TextWrapper 的 Text
属性 已更改。