WPF_16_数据视图
数据视图在后台工作,用于协调绑定数据的集合。使用数据视图,可添加导航逻辑并实现过滤,排序以及分组。
View对象
当将集合绑定到ItemsControl控件时,会自动在后台创建数据视图-位于数据源和绑定的控件之间。数据视图支持排序,过滤以及分组,这些功能和数据对象本身是相互独立的。例如,可以将同一数据集合绑定到两个不同的列表,并对数据进行过滤以显示不同的记录。
所有视图都继承自 CollectionView 类,它有两个子类 ListCollectionView 和 BindingListCollectionView.
- 如果数据源实现了 IBindingList 接口,创建 BindingListCollectionView 视图。当绑定DataTable对象时会创建该视图
- 如果数据源实现了 IList 接口,创建 ListCollectionView 视图。当绑定ObservableCollection集合时会创建该视图
- 如果没有实现IBindingList或IList接口,但实现了IEnumerable接口,创建CollectionView视图。
使用代码检索视图对象
ListCollectionView view = (ListCollectionView)CollectionViewSource.GetDefaultView(lstProducts.ItemsSource);
也可以在XAML标记中以声明方式构建CollectionViewSource对象,然后绑定到控件。
<CollectionViewSource x:Key="GroupByRangeView">
<CollectionViewSource.SortDescriptions>
<!--SortDescription类不是WPF名称空间中的类-->
<component:SortDescription PropertyName="UnitCost" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="UnitCost" Converter="{StaticResource PriceConverter}"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<!--绑定到列表中-->
<ListBox ItemSource="{Binding Source={StaticResource GroupByRangeView}}"/>
但声明的方式仍需要编写代码来检索数据:
CollectionViewSource viewSource = (CollectionViewSource)this.FindResource("GroupByRangeView");
viewSource.Source = products;
视图提供了一系列的方法和属性:
- Count - 列表中的项数
- CurrentItem - 当前数据对象的引用
- CurrentIndex - 当前位置索引
- MoveCurrentToFirst()
- MoveCurrentToLast()
- MoveCurrentToNext()
- MoveCurrentToPrevious()
- MoveCurrentToPosition()
过滤,排序与分组
过滤集合
当将集合用作数据源时,可使用 Filter 属性设置过滤器。
private void SetFilter()
{
viewSource.Filter = new Predicate<object>(FilterProduct);
}
// 过滤事件
public bool FilterProduct(object item)
{
Product product = (Product)item;
return (product.UnitCost > 100);
}
// 为视图调用 Refresh() 方法,从而强制性地重新过滤列表
public void RefreshView()
{
viewSource.Refresh();
}
过滤DataTable对象
DataTable对象都与一个DataView对象相关联,DataTable对象使用BindingListCollectionView视图不支持 Filter 属性,而是提供了 CustomFilter 属性。CustomFilter属性不做任何工作,只是接收指定的过滤字符串,并使用这个过滤字符串设置 DataView.RowFilter 属性。
RowFilter 将基于字符串的过滤器表达式作为参数,类似于Select查询中用于构造 WHERE 子句的 SQL 代码段。
var view = CollectionViewSource.GetDefaultView(lstProducts.ItemsSource) as BindingListCollectionView;
view.CustomFilter = "UnitCost > " + minimumPrice.ToString();
排序
// 按照某个属性进行 升序 或 降序
view.SortDescriptions.Add(
new SortDescription("ModelName", ListSortDirection.Ascending)
);
// 自定义排序
// 需要实现 IComparer 接口
view.CustomSort = new SortByModelNameLength();
分组
和排序一样,可以根据单个属性值进行分组,也可以使用自定义的回调函数进行分组。
view.GroupDescriptions.Add(new PropertyGroupDescription("CategoryName"));
当使用分组时,列表为每个分组创建了单独的GroupItem对象,并且为列表添加了这些GroupItem对象。使用样式可为列表中的所有GroupItem对象应用格式。ItemsControl.GroupStyle类并不是样式,只是一个简单的包,属性如下:
属性 | 说明 |
---|---|
ContainerStyle | 设置被应用到为每个分组生成的GroupItem元素样式 |
ContainerStyleSelector | 提供一个类,根据分组选择准备使用的正确样式 |
HeaderTemplate | 为每个分组开头显示内容创建模板 |
HeaderTemplateSelector | 提供一个类,根据分组选择准备使用的正确的头模板 |
Panel | 改变用于包含分组的模板 |
<ListBox Name="lstProducts" DisplayMemberPath="ModelName">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" Margin="0,5,0,5"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
这种根据某个属性进行简单分组,需要这个字段多条记录有同一个值。但如果想进行范围分组,就需要提供值转换器。
public class PriceRangeProductGrouper : IValueConverter
{
public int GorupInterval { get; set; }
public object Conveert(object value, Type targetType, object parameter, CultureInfo culture)
{
decimal price = (decimal)value;
if( price < GroupInterval)
{
return String.Format(culture, "Less than {0:C}", GroupInterval);
}
{
int intval = (int)price / GroupInterval;
int lowerLimit = interval * GroupInterval;
int upperLimit = (interval + 1) * GroupInterval;
return String.Format(culture, "{0:C} to {1:C}", lowerLimit, upperLimit);
}
}
}
在使用范围分组时,首先根据价格对产品进行排序,否则会根据它们在列表中的位置进行分组。
view.SortDescriptions.Add(new SortDescription("UnitCost", ListSortDirection.Ascending));
var gouper = new PriceRangeProductGrouper();
grouper.GroupInterval = 50;
view.GroupDescriptions.Add(new PropertyGroupDescription("UnitCost", grouper));
在控件使用分组时不启用虚拟化,WPF使用 VirtualizingStackPanel.IsVirtualizingWhenGrouping 属性纠正了这个问题。
<ListBox VirtualizingStackPanel.IsVirtualizingWhenGrouping="True"/>
如果某个编辑器将价格降低至过滤条件需要的最低值以下,理论上这条记录应该从视图中消失,但除非强制执行更新(调用ICollectionViewSource.Refresh()),否则看不到任何更改。WPF 4.5引入了实时成型的功能,监视特定属性的变化,如果发现变化就会触发刷新操作。实时成型需要满足以下三项标准:
- 数据对象必须实现 INofityPropertyChanged
- 集合必须实现 ICollectionVeiwLiveShaping
- 明确启用实时成型
实时成型增加了额外的开销,因此设计了三个属性 IsLiveFiltering, IsLiveSorting, IsLiveGrouping.这样可以忽略不重要的属性,确保获得最佳性能。
view.IsLiveFiltering = true;
view.LiveFilteringProperties.Add("UnitCost");
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结