当列表中某个项目的 属性 更改时,ListCollectionView 上的 LiveFiltering 不会重新评估过滤器

LiveFiltering on ListCollectionView doesn't reevaluate the Filter when a property of an item in the list changes

我有一个 class 人,它为 属性 姓名实施 INotifyPropertyChanged:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string property)
    {
        Console.WriteLine("PropertyChanged: " + Name + ": " + property);
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }

    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            if (_name == value)
                return;
            _name = value;
            OnPropertyChanged("Name");
        }
    }
}

我正在使用带 LiveFiltering 的 ListCollectionView 来显示 ObservableCollection,过滤后仅匹配姓名以 "A":

开头的人
var coll = new ObservableCollection<Person>();
ListCollectionView view = new ListCollectionView(coll)
{
    Filter = p => ((Person)p).Name[0]=='A',
    IsLiveFiltering = true,
    LiveFilteringProperties = { nameof(Person.Name) }
};

将项目添加到集合中后,它们会被正确过滤。
问题出在这里: 当一个人的名字被更改时,过滤器不会被重新评估,这就是我认为 IsLiveFiltering 和 LiveFilteringProperties 最初的目的。 因此,如果我将名称从 "Anna" 更改为 "Elsa",我希望视图会更新并且不再包含该项目。同样,将 "Eric" 更改为 "Arnold" 应该会更新视图,以便更改的项目成为视图中的容器。

var p1 = new Person { Name = "Anna" };
var p2 = new Person { Name = "Eric" };

coll.Add(p1); // view is updated automatically and contains Anna now
coll.Add(p2); // view is updated, but Eric is filtered out

view.Dump(); // shows only "Anna" (LINQPad - Dump)

p1.Name = "Elsa"; // change Anna to Elsa -> the instance p1 should be removed from view (not from collection)
p2.Name = "Arnold"; // change Eric to Arnold -> the instance p2 should now be in the view

//view.Refresh(); // uncommenting this line leads to the behaviour I actually expected from LiveFiltering to be handled automatically.

view.Dump(); // shows "Elsa", but we are filtering for A*

我是否错过了启用某些东西来获得这种行为?我真的不想手动附加到每个 Person 实例的 PropertyChanged - 我仍然希望这就是 LiveFiltering 为我所做的。

编辑: 我在使用更大模型的应用程序中遇到了这个问题,并提取了相关部分以在 LinqPad 中重现该问题。这是完整的 LinqPad 脚本。它还需要使用子句:

using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;

这是脚本

void Main()
{
    var coll = new ObservableCollection<Person>();
    ListCollectionView view = new ListCollectionView(coll)
    {
        IsLiveFiltering = true,
        LiveFilteringProperties = { nameof(Person.Name) },
        Filter = p => ((Person)p).Name[0] == 'A'
    };

    var p1 = new Person { Name = "Anna" };
    var p2 = new Person { Name = "Eric" };

    coll.Add(p1);
    coll.Add(p2);

    view.Dump();

    p1.Name = "Elsa";
    p2.Name = "Arnold";

    //view.Refresh();

    view.Dump();
    Debug.Assert(view.Cast<Person>().Single().Name == "Arnold", "Wrong item in view, expected Arnold");
}

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string property)
    {
        Console.WriteLine("PropertyChanged: " + Name + ": " + property);
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }

    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            if (_name == value)
                return;
            _name = value;
            OnPropertyChanged("Name");
        }
    }
}

您的视图和样本数据在哪里...?

下面的代码肯定能按预期工作。

XAML:

<ListBox x:Name="lb" DisplayMemberPath="Name" />
<Button Content="Filter" Click="Button_Click" />

示例代码:

public partial class MainWindow : Window
{
    Person _p = new Person() { Name = "Anna" };

    public MainWindow()
    {
        InitializeComponent();

        var coll = new ObservableCollection<Person>() { _p };
        ListCollectionView view = new ListCollectionView(coll)
        {
            Filter = p => ((Person)p).Name[0] == 'A',
            IsLiveFiltering = true,
            LiveFilteringProperties = { nameof(Person.Name) }
        };

        lb.ItemsSource = view;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        _p.Name = "Elsa";
    }
}

在 post 另一个问题之前,您可能应该阅读 this