1. 如何让列表的内容更容易查找
假设有这么一个列表(数据源在本地),由于内容太多,要查找到其中某个想要的数据会比较困难。要优化这个列表,无非就是排序、筛选和高亮。
改造过的结果如上。
2. 排序在WPF中要实现数据排序的功能有很多种,例如用Linq,但这种场景的标准做法是使用CollectionViewSource。
CollectionViewSource是一种数据集合的代理类。它有两个很重要的属性:
是数据源的集合;
是经过处理后的数据视图。
看上去感觉是不是很像数据库里的Table和View的关系?
在这个例子里使用CollectionViewSource排序的代码如下:
private readonly CollectionViewSource _viewSource; public HighlightSample() { InitializeComponent(); _viewSource = new CollectionViewSource { Source = Employee.AllExecutives }; _viewSource.View.Culture = new System.Globalization.CultureInfo("zh-CN"); _viewSource.View.SortDescriptions.Add(new SortDescription(nameof(Employee.FirstName), ListSortDirection.Ascending)); EmployeeElement.ItemsSource = _viewSource.View; }这段代码为CollectionViewSource的Source赋值后,把CollectionViewSource的View作为ListBox的数据源。其中用于描述View的排序方式。如果包含中文,别忘记将Culture设置为zh-cn。
至此排序的功能就实现了。文档中还提到CollectionViewSource的其它信息:
您可以将集合视图作为绑定源集合,可用于导航和显示集合中基于排序、 筛选和分组查询,而无需操作基础源集合本身的所有顶层。 如果Source实现INotifyCollectionChanged接口,所做的更改引起CollectionChanged事件传播到View。
由于View不会更改Source,因此每个Source都可以有多个关联的View。 使用View,可以通过不同方式显示相同数据。 例如,可能希望在页面左侧显示按优先级排序的任务,而在页面右侧显示按区域分组的任务。
3. 筛选CollectionViewSource的View属性类型为ICollectionView接口,它提供了属性用于实现数据的过滤。在这个例子里实现如下:
_viewSource.View.Filter = (obj) => (obj as Employee).DisplayName.ToLower().Contains(FilterElement.Text); private void OnFilterTextChanged(object sender, TextChangedEventArgs e) { if (_viewSource != null) _viewSource.View.Refresh(); }这段代码实现了当输入框的文字改变时刷新View的功能。其中方法用于重新创建View,也就是刷新视图。
ICollectionView还提供了一个函数,这个函数用于进入延迟循环,该循环可用于将更改合并到视图并延迟自动刷新,在需要多次操作并刷新数据量大的集合时可以用这个函数。
4. 高亮 <TextBox x:Name="FilterElement" TextChanged="OnFilterTextChanged"/> <ListBox Grid.Row="1" Margin="0,8,0,0"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding DisplayName}" kino:TextBlockService.HighlightText="{Binding ElementName=FilterElement,Path=Text}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>UWP的高亮可以使用TextHighlighter这个类,实现起来很简单。WPF中的高亮则是使用TextBlockService.HighlightText附加属性声明要高亮的文字,然后将TextBlock的Text替换为处理过的Inlines,使用方式如上。
private static void MarkHighlight(TextBlock target, string highlightText) { var text = target.Text; target.Inlines.Clear(); if (string.IsNullOrWhiteSpace(text)) return; if (string.IsNullOrWhiteSpace(highlightText)) { target.Inlines.Add(new Run { Text = text }); return; } while (text.Length > 0) { var runText = string.Empty; var index = text.IndexOf(highlightText, StringComparison.InvariantCultureIgnoreCase); if (index > 0) { runText = text.Substring(0, index); target.Inlines.Add(new Run { Text = runText, Foreground = _noHighlightBrush }); } else if (index == 0) { runText = text.Substring(0, highlightText.Length); target.Inlines.Add(new Run { Text = runText }); } else if (index == -1) { runText = text; target.Inlines.Add(new Run { Text = runText, Foreground = _noHighlightBrush }); } text = text.Substring(runText.Length); } }这是实现代码。其实用Regex.Split代码会好看很多,但懒得改了。
本来应该是高亮匹配的文字,但实际使用中发觉把未匹配的文字置灰更好看,就这样实现了。
这篇文章介绍了使用CollectionViewSource实现的排序、筛选功能,以及使用附加属性和Inlines实现高亮功能。