You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中基于INotifyPropertyChanged实现ListView自动筛选更新求助

问题分析

你当前的实现中,CollectionViewSource的筛选逻辑仅会在集合新增/移除元素时自动触发,不会监听集合内元素的IsChosen属性变化。因此当元素的IsChosen值修改后,ListView不会自动更新筛选结果。

解决方案

下面提供两种可行的解决方式,按需选择:

方式一:在View层监听元素属性变化(快速修复)

在UserControl中监听Elements集合的元素新增/移除事件,以及每个元素的PropertyChanged事件,当IsChosen变化时手动刷新视图:

public YourUserControl()
{
    InitializeComponent();
    Loaded += OnUserControlLoaded;
}

private void OnUserControlLoaded(object sender, RoutedEventArgs e)
{
    var viewModel = DataContext as ViewModel;
    if (viewModel?.Elements == null) return;

    // 为现有元素绑定属性变更监听
    foreach (var elem in viewModel.Elements)
    {
        elem.PropertyChanged += OnChoosableElementPropertyChanged;
    }

    // 监听集合的新增/移除事件,动态绑定/解绑元素的属性监听
    viewModel.Elements.CollectionChanged += OnElementsCollectionChanged;
}

private void OnElementsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach (ChoosableElement elem in e.NewItems)
        {
            elem.PropertyChanged += OnChoosableElementPropertyChanged;
        }
    }

    if (e.OldItems != null)
    {
        foreach (ChoosableElement elem in e.OldItems)
        {
            elem.PropertyChanged -= OnChoosableElementPropertyChanged;
        }
    }
}

private void OnChoosableElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    // 仅当IsChosen属性变化时刷新视图
    if (e.PropertyName == nameof(ChoosableElement.IsChosen))
    {
        var viewSource = Resources["ChosenElementsViewSource"] as CollectionViewSource;
        viewSource?.View.Refresh();
    }
}

方式二:在ViewModel层管理筛选(MVVM规范)

将筛选逻辑移至ViewModel,通过ICollectionView统一处理集合和元素的变化,更符合MVVM模式:

1. 修改ViewModel实现

private class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<ChoosableElement> _elements;
    public ObservableCollection<ChoosableElement> Elements
    {
        get => _elements;
        set
        {
            if (_elements != value)
            {
                // 解绑旧集合的监听
                if (_elements != null)
                {
                    _elements.CollectionChanged -= OnElementsCollectionChanged;
                    UnsubscribeElementPropertyChanges(_elements);
                }

                _elements = value;

                // 绑定新集合的监听
                if (_elements != null)
                {
                    _elements.CollectionChanged += OnElementsCollectionChanged;
                    SubscribeElementPropertyChanges(_elements);
                    // 创建并配置筛选视图
                    ChosenElementsView = CollectionViewSource.GetDefaultView(_elements);
                    ChosenElementsView.Filter = FilterChosenElements;
                }

                OnPropertyChanged(nameof(Elements));
                OnPropertyChanged(nameof(ChosenElementsView));
            }
        }
    }

    // 供View绑定的筛选视图
    public ICollectionView ChosenElementsView { get; private set; }

    // 筛选逻辑
    private bool FilterChosenElements(object item)
    {
        return item is ChoosableElement elem && elem.IsChosen;
    }

    // 绑定元素的属性变化监听
    private void SubscribeElementPropertyChanges(IEnumerable<ChoosableElement> elements)
    {
        foreach (var elem in elements)
        {
            elem.PropertyChanged += OnChoosableElementPropertyChanged;
        }
    }

    // 解绑元素的属性变化监听
    private void UnsubscribeElementPropertyChanges(IEnumerable<ChoosableElement> elements)
    {
        foreach (var elem in elements)
        {
            elem.PropertyChanged -= OnChoosableElementPropertyChanged;
        }
    }

    private void OnElementsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
            SubscribeElementPropertyChanges(e.NewItems.Cast<ChoosableElement>());
        if (e.OldItems != null)
            UnsubscribeElementPropertyChanges(e.OldItems.Cast<ChoosableElement>());
    }

    private void OnChoosableElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(ChoosableElement.IsChosen))
        {
            ChosenElementsView?.Refresh();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. 修改ListView绑定

移除XAML中的CollectionViewSource定义,直接绑定ViewModel的ChosenElementsView:

<ListView x:Name="ChosenListView" ItemsSource="{Binding ChosenElementsView}" />
额外注意事项
  • 确保修改IsChosen属性的操作在UI线程执行,若在后台线程修改需使用Dispatcher调度。
  • 你的ChoosableElement类的PropertyChanged实现是正确的,无需修改。

内容的提问来源于stack exchange,提问作者Idra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 09:40:55