- 示例工程
【免费下载链接】WPF-Samples
Repository for WPF related 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、AlternationCount | GroupStyle.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
相关推荐
WinUI 3 XAML Islands 实战指南:在 Win32、WPF 与 WinForms 中托管现代 XAML 体验
WinUI 3 XAML Islands 实战指南:在 Win32、WPF 与 WinForms 中托管现代 XAML 体验 在既有 Win32、WPF 或 W
前端UI组件桌面应用NestJS-Prisma与GraphQL完美结合:构建高效API服务
NestJS Prisma与GraphQL完美结合:构建高效API服务 NestJS Prisma是一款为NestJS应用提供便捷Prisma支持的工具,能帮助
WPF中的数据排序:从基础实现到高级应用
WPF中的数据排序:从基础实现到高级应用 一、痛点与解决方案概述 你是否在WPF(Windows Presentation Foundation,Windows
UI组件桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考