C# 在ListView C中筛选大型数据集合#
我试图使用一个过滤器字段,根据项目是否包含字符串过滤存储在C# 在ListView C中筛选大型数据集合#,c#,wpf,large-data,C#,Wpf,Large Data,我试图使用一个过滤器字段,根据项目是否包含字符串过滤存储在ObservableCollection中的大型数据列表,并在列表视图中显示结果 目前我正在使用一个转换器来实现这一点。它通过使用简单的不区分大小写的比较方法来检查目标字符串是否包含筛选器字符串 private static bool Contains(string source, string toCheck, StringComparison comp = StringComparison.OrdinalIgnoreCase) {
ObservableCollection
中的大型数据列表,并在列表视图中显示结果
目前我正在使用一个转换器来实现这一点。它通过使用简单的不区分大小写的比较方法来检查目标字符串是否包含筛选器字符串
private static bool Contains(string source, string toCheck, StringComparison comp = StringComparison.OrdinalIgnoreCase)
{
return source?.IndexOf(toCheck, comp) >= 0;
}
这种方法似乎适用于少量条目(几百条)。但是我处理的数据大小可以从5万到20万个条目
在搜索大约200000个条目的数据集合时,是否有一种方法可以有效地过滤列表,而不会产生很大的性能影响
下面是MCVE
XAML
<Window x:Class="FastFilter.MainWindow"
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:FastFilter"
mc:Ignorable="d"
Title="Fast Filter" Height="450" Width="800">
<Window.Resources>
<local:FilterConverter x:Key="FilterConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Text="{Binding Path=FilterString, UpdateSourceTrigger=PropertyChanged}"/>
<ListView Grid.Row="1"
ItemsSource="{Binding Path=Infos}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource FilterConverter}">
<Binding Path="DataContext.FilterString" RelativeSource="{RelativeSource AncestorType=ListView}"/>
<Binding Path="Text"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<!-- List Box Item Layout -->
<StackPanel Orientation="Horizontal">
<Label Content="Text:"/>
<Label Content="{Binding Text}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
CS
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
namespace FastFilter
{
public partial class MainWindow : INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
for (int i = 0; i < 200000; i++)
{
Infos.Add(new ObjectInfo(Guid.NewGuid().ToString()));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string filterString = string.Empty;
public string FilterString
{
get => filterString;
set
{
filterString = value;
OnPropertyChanged();
}
}
private ObservableCollection<ObjectInfo> infos = new ObservableCollection<ObjectInfo>();
public ObservableCollection<ObjectInfo> Infos {
get => infos;
set {
infos = value;
OnPropertyChanged();
}
}
}
public class ObjectInfo
{
public ObjectInfo(string text)
{
Text = text;
}
public string Text { get; }
}
public class FilterConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string filter = (string)values[0];
string checkStringContains = (string)values[1];
return !(string.IsNullOrWhiteSpace(checkStringContains) || string.IsNullOrWhiteSpace(filter))
? Contains(checkStringContains, filter) ? Visibility.Visible : Visibility.Collapsed
: Visibility.Visible;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
private static bool Contains(string source, string toCheck, StringComparison comp = StringComparison.OrdinalIgnoreCase)
{
return source?.IndexOf(toCheck, comp) >= 0;
}
}
}
使用系统;
使用System.Collections.ObjectModel;
使用系统组件模型;
利用制度全球化;
使用System.Runtime.CompilerServices;
使用System.Windows;
使用System.Windows.Data;
名称空间快速过滤器
{
公共部分类主窗口:INotifyPropertyChanged
{
公共主窗口()
{
初始化组件();
DataContext=this;
对于(int i=0;i<200000;i++)
{
添加(新ObjectInfo(Guid.NewGuid().ToString());
}
}
公共事件属性更改事件处理程序属性更改;
私有void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
私有字符串过滤器string=string.Empty;
公共字符串筛选器字符串
{
get=>filterString;
设置
{
filterString=值;
OnPropertyChanged();
}
}
私有ObservableCollection infos=新ObservableCollection();
公共可观测收集信息{
获取=>infos;
设置{
infos=价值;
OnPropertyChanged();
}
}
}
公共类ObjectInfo
{
公共ObjectInfo(字符串文本)
{
文本=文本;
}
公共字符串文本{get;}
}
公共类筛选器转换器:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
字符串筛选器=(字符串)值[0];
字符串checkStringContains=(字符串)值[1];
return!(string.IsNullOrWhiteSpace(checkStringContains)| | string.IsNullOrWhiteSpace(filter))
?包含(checkStringContains,filter)?可见性。可见:可见性。折叠
:能见度。可见;
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotSupportedException();
}
私有静态bool包含(字符串源、字符串toCheck、StringComparison comp=StringComparison.OrdinalIgnoreCase)
{
返回源?.IndexOf(toCheck,comp)>=0;
}
}
}
对于这种特殊查询,您必须扫描整个集合,以建立筛选项目集,因此在那里您可以做的不多
我建议提高效率,不要在每次(单个字符)更改过滤器字符串后立即重新执行过滤器。相反,每次更改FilterString(重新)启动一个计时器对象,时间为1秒,并且只在计时器滴答作响时进行过滤。或者您可以使用某种缓冲反应式扩展构造来实现相同的结果。尝试使用ICollectionView
xaml
下一个升级将是向textchanged事件添加Dispatchermer,这样筛选器仅在文本未输入约一秒钟后更新,而不是针对每个字符进行更新。WPF中的排序、分组、筛选和选择跟踪通过CollectionView完成。UI类将自动创建(甚至共享)单个CollectionView,但要有效地使用它,您必须控制它的创建。事实上,您的目标应该是公开CollectionView,而不是原始的ObservableCollection。使用Hashset怎么样。
Contains将是O(1)。“但是我正在处理的数据大小可能在5万到20万个条目之间。”如果您要检索那么多条目,那么DB access的基本设计显然是错误的。实际上,您不能向用户显示超过100个字段的数据,并期望用户处理这些数据。如果要在该级别上进行过滤,请始终在DB查询中进行过滤。不要批量检索,然后在UI中进行过滤。使用分页和DBMS支持的任何其他方法都不能检索到那么多的数据。这可能很有用,它只会加载指定数量的要显示的记录。@Christopher感谢您提供的信息。我将重新设计它,以便DB处理它
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox TextChanged="FilterTextChanged" Text="{Binding Path=FilterString, UpdateSourceTrigger=PropertyChanged}"/>
<ListView
x:Name="InfosListView"
Grid.Row="1"
ItemsSource="{Binding Path=Infos}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<!-- List Box Item Layout -->
<StackPanel Orientation="Horizontal">
<Label Content="Text:"/>
<Label Content="{Binding Text}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
private void FilterTextChanged(object sender, TextChangedEventArgs e)
{
UpdateFilter();
}
private void UpdateFilter()
{
//NOTE: bellow comment only applies to DataGrids.
//Calling commit or cancel edit twice resolves exceptions when trying to filter the DataGrid.
//https://stackoverflow.com/questions/20204592/wpf-datagrid-refresh-is-not-allowed-during-an-addnew-or-edititem-transaction-m
//CommitEdit();
//CommitEdit();
ICollectionView view = CollectionViewSource.GetDefaultView(Infos);
if (view != null)
{
view.Filter = delegate (object item)
{
if (item is ObjectInfo objectInfo)
{
return objectInfo.Text.Contains(FilterString);
}
return false;
};
}
}