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

WPF中CollectionViewSource筛选不自动更新,如何实现动态刷新?

WPF中实现FilterText变更时自动刷新CollectionView筛选

问题描述

在WPF项目中使用CollectionViewSource实现筛选功能,希望当筛选属性FilterText变更时自动应用筛选逻辑。已实现FilterText的属性变更通知,但目前筛选仅在手动刷新数据时生效,需要实现FilterText更新时数据动态刷新。

现有代码

XAML代码

<Window x:Class="GPI_EE_TOOLS_V3.Views.E3BomPartView"
        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:GPI_EE_TOOLS_V3.Views"
        xmlns:vm="clr-namespace:GPI_EE_TOOLS_V3.ViewModels"
        mc:Ignorable="d"
        Title="E3BomPartView" Height="450" Width="800">
    <Window.Resources>
        <vm:E3BomPartVM x:Key="vm" />
        <CollectionViewSource Filter="e3BomPartsCollectionView_Filter" 
                              x:Name="e3BomPartsCollectionView" 
                              Source="{Binding Source={StaticResource vm}, Path=e3BomParts}" 
                              x:Key="cvs" IsLiveFilteringRequested="True" />
    </Window.Resources>
    <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="4*" />
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0">
            <Button Command="{Binding GetBomFromE3Command}" Height="25" Width="Auto" Content="Get E3 BOM"></Button>
            <Separator Margin="0,5,0,0"></Separator>
            <TextBlock HorizontalAlignment="Center" Text="Filter Text"></TextBlock>
            <TextBox x:Name="tb_filtertext" Text="{Binding FilterText}"></TextBox>
        </StackPanel>
        <DataGrid ItemsSource="{Binding Source={StaticResource cvs}}" Grid.Column="1" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Device" Binding="{Binding Componentname}"></DataGridTextColumn>
                <DataGridTextColumn Header="Sequence" Binding="{Binding Location}" Width="SizeToHeader"></DataGridTextColumn>
                <DataGridTextColumn Header="GPIPN" Binding="{Binding Gpipn}"></DataGridTextColumn>
                <DataGridTextColumn Header="Supplier" Binding="{Binding Supplier}" Width="150"></DataGridTextColumn>
                <DataGridTextColumn Header="Supplier PN" Binding="{Binding Supplierpn}" Width="300"></DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

ViewModel代码

public string FilterText
{
    get => filterText;
    set
    {
        filterText = value;
        OnPropertyChanged(nameof(FilterText));
    }
}

后台代码

private void e3BomPartsCollectionView_Filter(object sender, FilterEventArgs e)
{
    E3BomPart e3BomPart = e.Item as E3BomPart;
    if (e3BomPart != null && this.tb_filtertext.Text != "")
    {
        bool test = Fuzz.PartialTokenSetRatio(this.tb_filtertext.Text.ToUpper(),e3BomPart.Key) > 90;
        e.Accepted = test;
    }
}

问题原因

  1. 筛选逻辑直接读取View中控件的文本值,未与ViewModel的FilterText联动,不符合MVVM规范;
  2. IsLiveFilteringRequested="True"仅针对集合项自身属性变化自动刷新筛选,无法响应筛选条件(FilterText)的变更;
  3. FilterText变更时,未触发CollectionView重新执行筛选逻辑。

解决方案一:ViewModel中管理CollectionView(推荐,符合MVVM规范)

将集合视图和筛选逻辑移至ViewModel,实现完全解耦。

1. 修改ViewModel代码

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

public class E3BomPartVM : INotifyPropertyChanged
{
    private string _filterText;
    private ObservableCollection<E3BomPart> _e3BomParts;
    public ICollectionView E3BomPartsView { get; private set; }

    public string FilterText
    {
        get => _filterText;
        set
        {
            _filterText = value;
            OnPropertyChanged(nameof(FilterText));
            // 筛选条件变更时刷新视图
            E3BomPartsView?.Refresh();
        }
    }

    public ObservableCollection<E3BomPart> e3BomParts
    {
        get => _e3BomParts;
        set
        {
            _e3BomParts = value;
            OnPropertyChanged(nameof(e3BomParts));
            // 初始化集合视图并绑定筛选逻辑
            E3BomPartsView = CollectionViewSource.GetDefaultView(_e3BomParts);
            E3BomPartsView.Filter = FilterE3BomParts;
        }
    }

    private bool FilterE3BomParts(object item)
    {
        if (item is not E3BomPart part) return false;
        // 空文本时显示全部数据
        if (string.IsNullOrEmpty(FilterText)) return true;
        return Fuzz.PartialTokenSetRatio(FilterText.ToUpper(), part.Key) > 90;
    }

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

2. 修改XAML代码

移除资源中的CollectionViewSource,直接绑定ViewModel的集合视图:

<Window x:Class="GPI_EE_TOOLS_V3.Views.E3BomPartView"
        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:GPI_EE_TOOLS_V3.Views"
        xmlns:vm="clr-namespace:GPI_EE_TOOLS_V3.ViewModels"
        mc:Ignorable="d"
        Title="E3BomPartView" Height="450" Width="800">
    <Window.Resources>
        <vm:E3BomPartVM x:Key="vm" />
    </Window.Resources>
    <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="4*" />
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0">
            <Button Command="{Binding GetBomFromE3Command}" Height="25" Width="Auto" Content="Get E3 BOM"></Button>
            <Separator Margin="0,5,0,0"></Separator>
            <TextBlock HorizontalAlignment="Center" Text="Filter Text"></TextBlock>
            <!-- 添加UpdateSourceTrigger实现实时输入更新 -->
            <TextBox Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
        </StackPanel>
        <DataGrid ItemsSource="{Binding E3BomPartsView}" Grid.Column="1" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Device" Binding="{Binding Componentname}"></DataGridTextColumn>
                <DataGridTextColumn Header="Sequence" Binding="{Binding Location}" Width="SizeToHeader"></DataGridTextColumn>
                <DataGridTextColumn Header="GPIPN" Binding="{Binding Gpipn}"></DataGridTextColumn>
                <DataGridTextColumn Header="Supplier" Binding="{Binding Supplier}" Width="150"></DataGridTextColumn>
                <DataGridTextColumn Header="Supplier PN" Binding="{Binding Supplierpn}" Width="300"></DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

3. 移除后台筛选事件

此时无需保留View后台的e3BomPartsCollectionView_Filter方法,可直接删除。


解决方案二:在View后台监听FilterText变化(快速修改方案)

若不想改动ViewModel结构,可在View后台监听FilterText的变更事件,手动触发CollectionView刷新。

1. 修改View后台代码

public partial class E3BomPartView : Window
{
    private E3BomPartVM _viewModel;
    private CollectionViewSource _cvs;

    public E3BomPartView()
    {
        InitializeComponent();
        _viewModel = (E3BomPartVM)Resources["vm"];
        _cvs = (CollectionViewSource)Resources["cvs"];
        // 订阅ViewModel的属性变更事件
        _viewModel.PropertyChanged += ViewModel_PropertyChanged;
    }

    private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(E3BomPartVM.FilterText))
        {
            // 刷新CollectionView应用新筛选条件
            _cvs.View.Refresh();
        }
    }

    private void e3BomPartsCollectionView_Filter(object sender, FilterEventArgs e)
    {
        if (e.Item is not E3BomPart part)
        {
            e.Accepted = false;
            return;
        }

        if (string.IsNullOrEmpty(_viewModel.FilterText))
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = Fuzz.PartialTokenSetRatio(_viewModel.FilterText.ToUpper(), part.Key) > 90;
        }
    }
}

2. 修改XAML的TextBox绑定

添加UpdateSourceTrigger=PropertyChanged实现输入实时更新:

<TextBox x:Name="tb_filtertext" Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}"></TextBox>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 04:13:16