13 KiB
ListView 列表视图
概述
列表视图(ListView)是一种用于展示和操作结构化数据集合的高级控件。相比基础的 ListBox,ListView 提供了更加丰富的企业级功能——分组、排序、过滤、分页、虚拟化、加载状态覆盖等,适用于需要处理大量数据并提供良好交互体验的场景。
AtomUI 的 ListView 控件对标 Ant Design 5.0 List 组件的设计规范,在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和数据操作能力。
设计原理
Ant Design 的列表设计哲学
Ant Design 对列表的定位是:「最基础的列表展示,可承载文字、列表、图片、段落,常用于后台数据展示页面」。其核心设计理念包括:
- 数据驱动:列表内容完全由数据源驱动,而非手动添加 UI 元素
- 可选的选择模式:支持单选、多选、Toggle 选择等多种模式
- 内置分组:支持按属性对数据进行分组展示,带有分组标题
- 过滤与排序:内置数据视图(CollectionView)支持,无需手动操作数据源
- 分页支持:内置分页器集成,支持大数据量的分页展示
- 空状态:当无数据时自动展示空状态指示器
Avalonia ItemsControl 基础能力
AtomUI 的 ListView 直接继承自 Avalonia 框架的 Avalonia.Controls.ItemsControl(而非 Avalonia.Controls.ListBox)。ListView 自行实现了完整的选择逻辑,以支持更灵活的分页选择和数据视图集成。
Avalonia ItemsControl 的核心职责:
Avalonia 的 ItemsControl 是所有集合控件的基类,它负责管理数据项集合、创建容器控件、以及通过 ItemsPresenter + Panel 进行布局。其继承链为:
Control → TemplatedControl → ItemsControl
ItemsControl 提供的基础能力:
| 能力 | 说明 |
|---|---|
Items / ItemsSource |
项目集合管理,支持直接添加和数据源绑定 |
ItemTemplate |
通过 IDataTemplate 定义项目的数据展示方式 |
ItemsPanel |
自定义项目面板布局(默认为 VirtualizingStackPanel) |
| 容器化 | 自动为每个数据项创建容器控件(ListViewItem),支持容器回收 |
| 虚拟化 | 通过 VirtualizingStackPanel 实现 UI 虚拟化,仅渲染可见项 |
AtomUI ListView 的扩展设计
AtomUI ListView 在 Avalonia ItemsControl 的基础上,做了大量扩展以对齐 Ant Design 规范并满足企业级需求:
| 扩展能力 | 实现方式 | 设计动机 |
|---|---|---|
| 完整选择系统 | 自实现 ISelectionModel + SelectionMode |
独立于 Avalonia ListBox 的选择逻辑,支持分页全局索引选择 |
| 三种尺寸 | ISizeTypeAware 接口 + SizeType 共享属性 |
全局统一的 Large / Middle / Small 尺寸系统 |
| 数据视图集成 | IListCollectionView + ListCollectionView |
自动包装数据源为可排序、可过滤、可分组、可分页的视图 |
| 分组展示 | IsGroupEnabled + GroupPropertySelector |
按指定属性对数据进行分组,带分组标题行 |
| 排序 | SortDescriptions 属性 |
支持多字段排序,通过 ListSortDescription 配置 |
| 过滤 | Filter + FilterValue + FilterValueSelector |
内置过滤器框架,支持自定义过滤逻辑 |
| 分页 | TopPagination / BottomPagination + PageSize |
集成 AbstractPagination 控件实现客户端分页 |
| 空状态指示 | EmptyIndicator + EmptyIndicatorTemplate |
数据为空时自动展示 Empty 控件 |
| 加载状态覆盖 | IsOperating + OperatingMsg + Spin |
异步操作时展示 Spin 加载遮罩 |
| 选中指示器 | IsShowSelectedIndicator + SelectedIndicator |
可选的勾选图标,增强选中态的视觉辨识度 |
| 无边框模式 | IsBorderless 属性 |
嵌入其他容器控件时隐藏边框 |
| 过渡动画 | IsMotionEnabled + ListViewItem 的 Transitions |
列表项背景色、前景色平滑过渡 |
| 虚拟化上下文 | IListVirtualizingContextAware 接口 |
虚拟化滚动时保存和恢复容器状态 |
| Design Token | ListViewToken + RegisterTokenResourceScope |
所有视觉值从全局/组件级 Token 派生,支持主题切换 |
功能详解
选择系统
ListView 自行实现了完整的选择系统(未使用 Avalonia 的 SelectingItemsControl),通过 ListView.Selecting.cs 分部类管理:
- 选择模式:通过
SelectionMode属性支持Single(单选)、Multiple(多选)、Toggle(切换选择)、AlwaysSelected(始终有选中项) - 选择模型:使用
ListViewSelectionModel(内部继承自SelectionModel<object?>),支持批量选择操作 - 全局索引:在分页模式下,选择基于全局索引(
GlobalIndex)而非页面内本地索引 - 键盘导航:支持方向键导航选择、Space/Enter 切换选择、Ctrl+A 全选(多选模式)
- 自动滚动:
AutoScrollToSelectedItem控制选中项变化时是否自动滚动到可视区域 - 文本搜索:
IsTextSearchEnabled启用后,键入文字可快速定位匹配项
数据视图(CollectionView)
当设置 ItemsSource 时,ListView 自动将数据源包装为 ListCollectionView(实现 IListCollectionView),提供:
- 透明包装:如果传入的已是
IListCollectionView,直接使用;否则自动创建ListCollectionView包装 - 过滤描述:通过
FilterDescriptions管理过滤条件 - 排序描述:通过
SortDescriptions管理排序规则 - 分组描述:通过
GroupDescriptions管理分组逻辑 - 分页:通过
PageSize和MoveToPage管理客户端分页
分组
通过 IsGroupEnabled = true 开启分组功能。分组依赖 GroupPropertySelector 委托,从每个数据项中提取分组键值。默认情况下,如果数据项实现了 IGroupHeader 接口,会使用其 Group 属性作为分组键。
分组项在列表中显示为不可选择的标题行,使用 GroupItemTemplate 渲染,样式使用较小的字号和描述性颜色(GroupHeaderColor Token)。
过滤
ListView 内置过滤器框架:
Filter:IValueFilter类型,定义过滤逻辑(如包含匹配、精确匹配等)FilterValue:过滤关键字,设置后触发过滤FilterValueSelector:委托,从数据项中提取要过滤的属性值(默认提取IListItemData.Content)
AXAML 中可使用 {atom:ValueFilterProvider Contains} 标记扩展快速绑定内置过滤器。
分页
ListView 支持顶部/底部分页器集成:
TopPagination/BottomPagination:放置Pagination控件实例PageSize:每页数据量,设置后自动启用分页PaginationVisibility:控制分页器显示位置(None/Top/Bottom/Both)TopPaginationAlign/BottomPaginationAlign:分页器对齐方式(Start/Center/End)
分页器与 IListCollectionView 双向联动:翻页时自动通知数据视图切换页面。
加载状态(Operating)
当执行异步操作(如加载数据、保存等)时,可通过 IsOperating = true 展示加载状态覆盖层:
- 模板内置
Spin控件作为加载指示器 OperatingMsg显示加载提示文本CustomOperatingIndicator/CustomOperatingIndicatorTemplate支持自定义加载指示器
空状态
当列表数据为空(TotalItemCount == 0)且 IsShowEmptyIndicator == true 时,自动显示空状态指示器:
- 默认使用
Empty控件的 Simple 预设图 - 可通过
EmptyIndicator/EmptyIndicatorTemplate自定义空状态展示 EmptyIndicatorPadding控制空状态内边距
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
| 基础列表 | ✅ List + List.Item |
✅ ListView + ListViewItem |
✅ 完全对齐 |
三种尺寸 size |
✅ large / default / small |
✅ SizeType 属性 |
✅ 完全对齐 |
边框控制 bordered |
✅ 布尔属性 | ✅ IsBorderless 属性(语义相反) |
✅ 对齐 |
数据源 dataSource |
✅ 数组 | ✅ ItemsSource + IListCollectionView |
✅ 对齐(增强) |
渲染项 renderItem |
✅ 函数 | ✅ ItemTemplate 数据模板 |
✅ 对齐(方式不同,语义一致) |
分页 pagination |
✅ 对象配置 | ✅ TopPagination / BottomPagination |
✅ 对齐 |
加载状态 loading |
✅ 布尔/对象 | ✅ IsOperating + Spin |
✅ 对齐 |
空状态 locale.emptyText |
✅ ReactNode | ✅ EmptyIndicator + EmptyIndicatorTemplate |
✅ 对齐 |
| 分组 | ⚠️ 无内置(需手动实现) | ✅ IsGroupEnabled + GroupPropertySelector |
✅ 增强 |
| 排序 | ⚠️ 无内置 | ✅ SortDescriptions |
✅ 增强 |
| 过滤 | ⚠️ 无内置 | ✅ Filter + FilterValue |
✅ 增强 |
| Grid 栅格布局 | ✅ grid 属性 |
❌ 暂未支持 | ⚠️ 待支持 |
操作列 actions / extra |
✅ List.Item 属性 | ❌ 通过 ItemTemplate 自定义实现 | ⚠️ 通过模板覆盖 |
头部/脚部 header / footer |
✅ ReactNode | ❌ 暂未支持 | ⚠️ 待支持 |
继承关系
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ItemsControl
└── AtomUI.Desktop.Controls.ListView
├── implements ISizeTypeAware
├── implements IMotionAwareControl
└── implements IListVirtualizingContextAware
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
ItemsControl |
项目集合管理、容器创建/回收、ItemTemplate 模板化呈现、ItemsPanel 面板布局、虚拟化基础设施 |
AtomUI.Desktop.Controls.ListView |
完整选择系统、Ant Design 视觉体系(三种尺寸)、数据视图(排序/过滤/分组/分页)、Design Token 集成、空状态指示、加载状态覆盖、选中指示器、虚拟化上下文管理 |
实现的共享接口:
| 接口 | 定义位置 | 作用 |
|---|---|---|
ISizeTypeAware |
AtomUI.Controls.Shared |
支持 SizeType(Small / Middle / Large)三种尺寸切换 |
IMotionAwareControl |
AtomUI.Controls.Shared |
支持动画开关 |
IListVirtualizingContextAware |
AtomUI.Controls |
虚拟化滚动时保存和恢复容器状态 |
源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 控件类(主文件) | src/AtomUI.Desktop.Controls/ListView/ListView.cs |
属性定义、容器管理、核心逻辑 |
| 控件类(选择) | src/AtomUI.Desktop.Controls/ListView/ListView.Selecting.cs |
选择系统实现 |
| 控件类(分页) | src/AtomUI.Desktop.Controls/ListView/ListView.Pagination.cs |
分页逻辑 |
| 控件类(虚拟化) | src/AtomUI.Desktop.Controls/ListView/ListView.Virtualizing.cs |
虚拟化上下文管理 |
| 选择模型 | src/AtomUI.Desktop.Controls/ListView/ListViewSelectionModel.cs |
内部选择模型实现 |
| 列表项 | src/AtomUI.Desktop.Controls/ListView/ListViewItem.cs |
列表项容器控件 |
| 伪类常量 | src/AtomUI.Desktop.Controls/ListView/ListPseudoClass.cs |
伪类定义 |
| 枚举 | src/AtomUI.Desktop.Controls/ListView/ListPaginationVisibility.cs |
分页可见性枚举 |
| Token 定义 | src/AtomUI.Desktop.Controls/ListView/ListViewToken.cs |
组件级 Design Token |
| 主题模板 | src/AtomUI.Desktop.Controls/ListView/Themes/ListViewTheme.axaml |
ListView ControlTheme AXAML |
| 列表项主题 | src/AtomUI.Desktop.Controls/ListView/Themes/ListViewItemTheme.axaml |
ListViewItem ControlTheme AXAML |
| 模板常量 | src/AtomUI.Desktop.Controls/ListView/Themes/ListViewThemeConstants.cs |
模板部件名称常量 |
| Gallery 示例 | controlgallery/AtomUIGallery/ShowCases/Views/DataDisplay/ListShowCase.axaml |
使用范例 |
模板结构
ListView 模板
Border#Frame ← 主框架(背景、边框、圆角、内边距)
└── DockPanel
├── ContentPresenter#TopPaginationPresenter ← 顶部分页器(DockPanel.Dock="Top")
├── ContentPresenter#BottomPaginationPresenter← 底部分页器(DockPanel.Dock="Bottom")
└── Spin ← 加载状态覆盖层
└── Panel
├── ScrollViewer (PART_ScrollViewer) ← 滚动容器(数据非空时可见)
│ └── ItemsPresenter ← 项目呈现器(使用 VirtualizingStackPanel)
└── ContentPresenter#EmptyIndicator ← 空状态指示器(数据为空时可见)
ListViewItem 模板
Border#Frame ← 项目框架(背景、圆角、内边距)
└── DockPanel
├── IconTemplatePresenter#SelectedIndicator ← 选中指示器(DockPanel.Dock="Right",选中时可见)
└── ContentPresenter#ContentPresenter ← 内容展示器
模板部件常量
| 常量名 | 值 | 说明 |
|---|---|---|
ListViewThemeConstants.ScrollViewerPart |
"PART_ScrollViewer" |
滚动容器 |
ListViewThemeConstants.TopPaginationPart |
"PART_TopPagination" |
顶部分页器 |
ListViewThemeConstants.BottomPaginationPart |
"PART_BottomPagination" |
底部分页器 |
ListViewThemeConstants.ListViewPart |
"PART_ListView" |
列表视图 |