ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

使用 CollectionViewSource 在 XAML 中排序与分组数据:基于 WPF-Samples 的完整实战指南

使用 CollectionViewSource 在 XAML 中排序与分组数据:基于 WPF-Samples 的完整实战指南
  • 示例工程

【免费下载链接】WPF-Samples

Repository for WPF related samples

项目地址:https://gitcode.com/gh_mirrors/wp/WPF-Samples
点击查看免费下载

本篇技术指南以 WPF-Samples 仓库中 CollectionViewSource 示例 为主体,系统讲解如何在 XAML 中借助 CollectionViewSource 为数据集合建立"视图",并直接以声明式方式完成排序(SortDescriptions)与分组(GroupDescriptions),无需编写任何 C# 逻辑。读完本文,你将掌握视图机制的核心原理、对 CLR 对象集合与 XML 数据两种数据源的排序/分组写法、分组结果的呈现方式(GroupStyle),以及在代码中动态增删排序与分组的进阶用法。

核心概念:视图(View)与 CollectionViewSource 的定位

在 WPF 的数据绑定体系中,**数据源(Source)与视图(View)**是两个需要区分开的概念:

  • 数据源是真正的集合对象,例如ObservableCollection<Place>、XmlDataProvider返回的 XML 数据;
  • 视图是对数据源的一层包装,负责提供**排序(Sorting)、分组(Grouping)、筛选(Filtering)以及当前项跟踪(Currency,即 CurrentItem)**能力。

绑定到数据源时,WPF 会自动为该集合创建一个"默认视图",可通过CollectionViewSource.GetDefaultView获取。而CollectionViewSource 则是让开发者在 XAML 中直接创建并配置视图的代理类——它是System.Windows.Data命名空间下的核心类型,示例的窗口根元素中即通过xmlns引入了相关命名空间,并在Window.Resources中声明视图(见 MainWindow.xaml)。

本示例的核心成果是:只写 XAML、零 C# 业务代码,即可让ListBox中的数据按指定字段排序、并自动按另一字段分组显示。

示例一:对 CLR 对象集合排序并分组

1. 数据模型

示例定义了一个简单的城市数据模型 Place.cs,包含CityName与State两个字符串属性;Places.cs 则继承ObservableCollection<Place>,在构造函数中填充了 11 个真实城市数据(西雅图、雷德蒙德、贝尔维尤等,分属 WA / OR / CA 三个州):

public class Places : ObservableCollection<Place> { public Places() { Add(new Place("Seattle", "WA")); Add(new Place("Redmond", "WA")); Add(new Place("Bellevue", "WA")); // ... 共 11 个城市 } }

ObservableCollection的选型保证了集合增删时视图与 UI 能自动刷新。

2. 在 XAML 中声明数据源与视图

在 MainWindow.xaml 的Window.Resources中,先声明数据源实例,再声明一个CollectionViewSource指向它:

<Window.Resources> <local:Places x:Key="Places"/> <CollectionViewSource Source="{StaticResource Places}" x:Key="Cvs"> <CollectionViewSource.SortDescriptions> <componentModel:SortDescription PropertyName="CityName"/> </CollectionViewSource.SortDescriptions> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="State"/> </CollectionViewSource.GroupDescriptions> </CollectionViewSource> </Window.Resources>

要点说明:

  • Source属性通过{StaticResource Places}绑定到数据源,也可以是集合属性或XmlDataProvider;
  • SortDescriptions集合中放入SortDescription,指定PropertyName="CityName",表示按城市名升序排列。SortDescription还支持Direction属性(取值ListSortDirection.Ascending/Descending),不写时默认为升序;
  • GroupDescriptions集合中放入PropertyGroupDescription,指定PropertyName="State",表示按州分组。

这里需要引入System.ComponentModel命名空间(示例通过xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"声明),因为SortDescription类型定义在该命名空间下。

3. 将 ListBox 绑定到视图

MainWindow.xaml 中通过{Binding Source={StaticResource Cvs}}将ListBox绑定到视图:

<ListBox ItemsSource="{Binding Source={StaticResource Cvs}}" DisplayMemberPath="CityName" Name="lb"> <ListBox.GroupStyle> <x:Static Member="GroupStyle.Default"/> </ListBox.GroupStyle> </ListBox>
  • DisplayMemberPath="CityName"表示列表项只显示城市名;
  • GroupStyle设置为GroupStyle.Default(通过x:Static引用静态成员),即使用 WPF 内置的默认分组呈现样式。

4. 运行效果分析

依据 Places.cs 的数据,运行后ListBox将呈现如下结构:

  • 分组一(State = CA):Los Angeles、San Diego、San Francisco、San Jose、Santa Ana
  • 分组二(State = OR):Portland
  • 分组三(State = WA):Bellevue、Bellingham、Kirkland、Redmond、Seattle

可见:组内顺序由SortDescription决定(城市名升序),组的分界由PropertyGroupDescription决定(按州归组),排序与分组可以同时作用于同一视图,互不冲突。

示例二:对 XML 数据排序并分组

同一个示例还在Window.Resources中演示了如何对XML 数据建立排序分组的视图(MainWindow.xaml)。

1. 用 XmlDataProvider 声明 XML 数据源

<XmlDataProvider x:Key="MyTasks" XPath="Tasks/Task"> <x:XData> <Tasks xmlns=""> <Task Name="Groceries" Priority="2" Type="Home"> <Description>Pick up Groceries and Detergent</Description> </Task> <!-- 共 6 个任务:Groceries / Laundry / Email / Clean / Dinner / Proposals --> </Tasks> </x:XData> </XmlDataProvider>

XmlDataProvider配合内联的x:XData,直接在 XAML 中构造一个任务列表 XML,每个<Task>节点带Name、Priority、Type三个属性。

2. 对 XML 属性排序与分组:@前缀语法

<CollectionViewSource x:Key="MySortedTasks" Source="{StaticResource MyTasks}"> <CollectionViewSource.SortDescriptions> <componentModel:SortDescription PropertyName="@Priority" /> </CollectionViewSource.SortDescriptions> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="@Priority" /> </CollectionViewSource.GroupDescriptions> </CollectionViewSource>

关键差异在于:当数据源是 XML 时,PropertyName需要写成@属性名的形式(如@Priority)——这是 XPath 中选取属性的语法约定,与 CLR 对象的属性路径(直接写CityName)不同。本视图按任务的Priority属性先排序、再分组,最终效果是按优先级(1 / 2 / 3)分成三个组,组内任务按优先级升序排列。

MySortedTasks视图随后同样被绑定到第二个ListBox(ItemsSource="{Binding Source={StaticResource MySortedTasks}}"),无需任何 C# 代码即可展示分组的 XML 任务列表。

提示:仓库中同主题的 Grouping 示例 展示了更为完整的 XML 分组呈现——它用XmlDataProvider的XPath="Tasks/Task"取节点、PropertyGroupDescription("@Type")按任务类型分组,并通过GroupStyle.HeaderTemplate自定义分组头样式,可作为本示例的延伸阅读。

呈现分组结果:GroupStyle 详解

视图负责"如何切分数据",而GroupStyle 负责"如何画分组"。本示例使用x:Static Member="GroupStyle.Default"采用默认样式;若要自定义分组头,可参考 Grouping/MainWindow.xaml 的写法:

<ItemsControl.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock FontWeight="Bold" FontSize="15" Text="{Binding Path=Name}"/> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ItemsControl.GroupStyle>

分组头模板的DataContext是CollectionViewGroup对象,其Name属性即分组依据的值(如"WA""CA"或优先级数字),因此模板内用{Binding Path=Name}即可显示组名。此外GroupStyle还提供AlternationCount(交替行样式)等属性,可用于美化分组列表。

在代码中动态操作视图(进阶补充)

虽然本示例全程使用 XAML 声明式写法,但仓库中的其他示例展示了同一套 API 在代码中的用法,有助于加深理解视图机制:

1. 获取默认视图:CollectionViewSource.GetDefaultView

Grouping/MainWindow.cs 演示了如何拿到绑定源对应的视图并动态增删分组:

_myView = (CollectionView) CollectionViewSource.GetDefaultView(myItemsControl.ItemsSource); if (_myView.CanGroup) // 先检查视图是否支持分组 { var groupDescription = new PropertyGroupDescription("@Type"); _myView.GroupDescriptions.Add(groupDescription); } // 移除分组:_myView.GroupDescriptions.Clear();
  • GetDefaultView返回数据源对应的默认视图,可强制转换为CollectionView或ListCollectionView;
  • CanGroup/CanSort/CanFilter属性用于查询视图能力,避免对不支持的操作报错。

2. 运行时动态排序与筛选

SortFilter/MainWindow.cs 展示了点击不同按钮切换排序字段:

MyCollectionView.SortDescriptions.Clear(); MyCollectionView.SortDescriptions.Add(new SortDescription("Customer", ListSortDirection.Ascending));

同文件还演示了通过MyCollectionView.Filter = Contains;设置筛选委托、以及MoveCurrentToNext()/MoveCurrentToPrevious()等当前项(Currency)导航方法。视图对排序、分组、筛选的修改会立即反映到所有绑定该视图的 UI 控件,这正是 WPF 视图机制的核心价值。

构建与运行示例

获取源码

最简单的方式是从仓库页面下载当前版本 ZIP 包并解压(无需安装 Git);也可以在本地使用 Git 克隆:

git clone https://gitcode.com/gh_mirrors/wp/WPF-Samples.git

解压或克隆后,使用Visual Studio 2022打开WPF-Samples解决方案即可开始编译。

编译与部署

  • 部署示例:在 Visual Studio 中选择Build > Deploy Solution;
  • 调试运行:按F5,或选择Debug > Start Debugging;
  • 免调试运行:按Ctrl+F5,或选择Debug > Start Without Debugging。

项目文件 CollectionViewSource.csproj 采用 SDK 风格项目,目标框架为net10.0-windows并启用UseWpf=true,在较新版本的 .NET SDK 下可直接还原并生成;应用入口由 App.xaml 的StartupUri="MainWindow.xaml"指定,窗口代码 MainWindow.cs 仅包含标准的InitializeComponent()调用,进一步印证了"全部逻辑都在 XAML 中完成"的示例设计。

关键 API 速查表

API / XAML 元素用途关键参数备注
CollectionViewSource在 XAML 中声明数据视图Source:数据源视图的排序/分组/筛选能力由 WPF 自动提供
SortDescription定义排序规则PropertyName、Direction默认Ascending;XML 属性需用@前缀
PropertyGroupDescription定义分组规则PropertyName按属性值归组;XML 属性同样用@前缀
GroupStyle控制分组的呈现HeaderTemplate、AlternationCountGroupStyle.Default为默认样式
CollectionViewSource.GetDefaultView代码中获取默认视图参数为集合对象返回ICollectionView
GroupDescriptions/SortDescriptions视图的集合属性支持Add/Clear修改后 UI 即时刷新
CanGroup/CanSort/CanFilter视图能力查询—操作前建议检查

参考文件

  • 示例说明:Data Binding/CollectionViewSource/README.md
  • 核心 XAML 实现:Data Binding/CollectionViewSource/MainWindow.xaml
  • 数据模型:Data Binding/CollectionViewSource/Place.cs、Data Binding/CollectionViewSource/Places.cs
  • 项目配置:Data Binding/CollectionViewSource/CollectionViewSource.csproj
  • 相关延伸示例:Data Binding/Grouping/MainWindow.xaml、Data Binding/Grouping/MainWindow.cs、Data Binding/SortFilter/MainWindow.cs
  • 示例工程

【免费下载链接】WPF-Samples

Repository for WPF related samples

项目地址:https://gitcode.com/gh_mirrors/wp/WPF-Samples
点击查看免费下载
上一篇:radix-vue 组件解析:TagsInputClear 一键清空标签的用法与源码实现
下一篇:gogcli 实战指南:用 `gog slides element` 命令组在终端中创建与操控 Google Slides 原生页面元素

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进