Files
AtomUI/docs/Desktop.Controls/ListView/ListView.md
T

239 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ListView 列表视图
## 概述
列表视图(ListView)是一种用于展示和操作结构化数据集合的高级控件。相比基础的 `ListBox`,ListView 提供了更加丰富的企业级功能——分组、排序、过滤、分页、虚拟化、加载状态覆盖等,适用于需要处理大量数据并提供良好交互体验的场景。
AtomUI 的 `ListView` 控件对标 [Ant Design 5.0 List](https://ant.design/components/list-cn) 组件的设计规范,在 .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"` | 列表视图 |