为什么 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 属性 已更改。