# 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`),支持批量选择操作 - **全局索引**:在分页模式下,选择基于全局索引(`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"` | 列表视图 |