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; } }
问题原因
- 筛选逻辑直接读取View中控件的文本值,未与ViewModel的
FilterText联动,不符合MVVM规范; IsLiveFilteringRequested="True"仅针对集合项自身属性变化自动刷新筛选,无法响应筛选条件(FilterText)的变更;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
相关产品推荐
相关产品推荐

