【发布时间】:2019-03-22 02:54:31
【问题描述】:
我正在尝试使用过滤器字段来过滤存储在ObservableCollection 中的大量数据,根据项目是否包含字符串并将结果显示在ListView 中。
目前我正在使用转换器来实现这一点。它通过使用简单的不区分大小写的比较方法检查目标字符串是否包含过滤器字符串来工作。
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;
}
}
}
【问题讨论】:
-
WPF 中的排序、分组、过滤和选择跟踪是通过 CollectionView 完成的。 UI 类将自动创建(甚至共享)单个 CollectionView,但要有效地使用它,您必须控制它的创建。实际上,您应该旨在公开 CollectionView,而不是原始的 ObservableCollection。
-
如果使用
Hashset<T>.Contains 会是 O(1)。 -
“但我正在处理的数据大小范围可以从 50,000 到 200,000 个条目。”如果您要检索那么多条目,那么您的数据库访问基本设计是完全错误的。您实际上不能向用户显示超过 100 个数据字段并期望他使用它。如果您要按该比例进行过滤,请始终在数据库查询中进行。不要批量检索然后在 UI 中进行过滤。使用分页和 DBMS 支持的任何其他方式来不检索那么多数据。
-
这可能很有用github.com/lvaleriu/Virtualization/tree/master/…,它只会加载要显示的指定数量的记录。
-
@Christopher 感谢您提供这些信息。我会考虑重新设计它以便数据库处理它
标签: c# wpf large-data