Files

275 lines
16 KiB
Markdown
Raw Permalink 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.
# ComboBox 组合框
## 概述
组合框(ComboBox)是最基础的下拉选择控件,用户通过点击触发下拉面板,从预定义的选项列表中选择一个值。它广泛用于表单填写、筛选条件设置、配置项选择等场景——当候选项较多时,下拉选择比单选框更节省空间;当需要精确匹配时,下拉选择比自由输入更可靠。
AtomUI 的 `ComboBox` 控件对齐 [Ant Design 5.0 Select (basic)](https://ant.design/components/select-cn) 的基础模式设计规范,在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和交互行为。
---
## 设计原理
### Ant Design 的 Select 设计哲学
Ant Design 的 Select 组件定位为:**「弹出一个下拉菜单给用户选择操作,用于代替原生的选择器」**。其核心设计理念包括:
**三种样式变体**(按视觉风格区分):
| 变体 | 设计意图 | 典型用途 |
|---|---|---|
| 🔲 **描边(Outlined)** | 默认样式,白色背景 + 实线边框,中等视觉权重 | 表单中的标准选择器 |
| 🟫 **填充(Filled)** | 灰色填充背景,无明显边框,较低视觉干扰 | 搜索栏、筛选器等辅助场景 |
| ➖ **无边框(Borderless)** | 无背景无边框,最轻量的视觉呈现 | 表格内嵌、只读展示等场景 |
**两种状态修饰**(用于表单验证反馈):
| 状态 | 设计意图 |
|---|---|
| ❌ **错误(Error)** | 红色系边框/底线,提示用户当前选择不满足验证规则 |
| ⚠️ **警告(Warning)** | 橙色系边框/底线,提示用户注意但不阻止提交 |
**AddOn 扩展区域**(前后附加内容):
| 区域 | 设计意图 |
|---|---|
| 🏷️ **前置标签(LeftAddOn)** | 选择器左侧外部附加区域,常用于协议前缀(如 `http://`) |
| 🏷️ **后置标签(RightAddOn)** | 选择器右侧外部附加区域,常用于域名后缀(如 `.com`) |
| 🔤 **内部前缀(ContentLeftAddOn)** | 内容区域左侧,常用于图标或货币符号 |
| 🔤 **内部后缀(ContentRightAddOn)** | 内容区域右侧,常用于单位或辅助图标 |
### Avalonia ComboBox 基础能力
AtomUI 的 `ComboBox` 继承自 Avalonia 框架的 `Avalonia.Controls.ComboBox`。理解 Avalonia ComboBox 的基础能力有助于理解 AtomUI 在其之上做了哪些扩展。
**Avalonia ComboBox 的核心职责:**
Avalonia 的 `ComboBox` 是一个标准的 `SelectingItemsControl`(选择型项目控件),它管理一组选项并允许用户从中选择一个。其继承链为:
```
Control → TemplatedControl → ItemsControl → SelectingItemsControl → ComboBox
```
作为 `SelectingItemsControl`,ComboBox 提供了完整的选择管理(`SelectedItem`、`SelectedIndex`、`SelectionChanged` 事件)、项目容器化(`ComboBoxItem`)、数据绑定(`ItemsSource` + `ItemTemplate`)、以及 `Popup` 弹出面板等基础设施。
**Avalonia ComboBox 提供的基础属性:**
| 属性 | 说明 |
|---|---|
| `ItemsSource` | 数据源集合,支持绑定任意 `IEnumerable` |
| `ItemTemplate` | 项目数据模板,定义每个选项的呈现方式 |
| `SelectedItem` | 当前选中项,支持双向绑定 |
| `SelectedIndex` | 当前选中项索引 |
| `PlaceholderText` | 未选中时显示的占位提示文本 |
| `IsDropDownOpen` | 下拉面板是否打开 |
| `MaxDropDownHeight` | 下拉面板最大高度 |
| `ItemsPanel` | 项目面板模板(默认为 `VirtualizingStackPanel`) |
**Avalonia ComboBox 提供的基础伪类:**
| 伪类 | 说明 |
|---|---|
| `:pointerover` | 鼠标悬浮在控件上 |
| `:pressed` | 控件被按下 |
| `:disabled` | `IsEnabled == false` |
| `:focus` | 控件获得焦点 |
| `:focus-visible` | 通过键盘(Tab)获得焦点 |
### AtomUI 的扩展设计
AtomUI `ComboBox` 在 Avalonia ComboBox 的基础上,遵循 Ant Design 5.0 规范做了以下扩展:
| 扩展能力 | 实现方式 | 设计动机 |
|---|---|---|
| **三种样式变体** | `InputControlStyleVariant` 枚举 + `AddOnDecoratedBox` 委托样式 | 对齐 Ant Design 的 `variant`,支持 Outlined / Filled / Borderless |
| **三种尺寸** | `ISizeTypeAware` 接口 + `SizeType` 共享属性 | 全局统一的 Large / Middle / Small 尺寸系统 |
| **验证状态** | `IInputControlStatusAware` 接口 + `Status` 属性 | 对齐 Ant Design 的 `status`,支持 Error / Warning 视觉反馈 |
| **AddOn 扩展区域** | `AddOnDecoratedBox` 装饰容器 + 8 个 AddOn 属性 | 复刻 Ant Design 的 addonBefore / addonAfter / prefix / suffix |
| **下拉手柄** | `ComboBoxHandle` 内置组件 + `IconButton` | 独立的下拉箭头按钮,带悬浮/按下颜色反馈 |
| **分页下拉高度** | `DropDownDisplayPageSize` 属性 + 自动计算 `MaxDropDownHeight` | 按项目数量控制下拉面板可见行数 |
| **过渡动画** | `IsMotionEnabled` + `Popup` 动画 | 下拉面板展开/收起的平滑过渡动画 |
| **表单集成** | `IFormItemAware` + `IFormItemFeedbackAware` 接口 | 可参与 FormItem 验证流程,自动显示反馈图标 |
| **Design Token** | `ComboBoxToken` + `RegisterTokenResourceScope` | 所有视觉值从全局/组件级 Token 派生,支持主题切换 |
---
## 功能详解
### 样式变体(StyleVariant)
样式变体通过 `StyleVariant` 属性设置,ComboBox 将该属性传递给内部的 `AddOnDecoratedBox` 容器,由后者统一管理边框/背景样式。三种变体的视觉差异:
| 变体 | 正常态 | 聚焦态 | 错误态 |
|---|---|---|---|
| `Outlined` | 白色背景 + 灰色边框 | 主色边框 + 外发光 | 红色边框 + 红色外发光 |
| `Filled` | 灰色填充背景 | 白色背景 + 主色边框 | 红色填充背景 + 红色边框 |
| `Borderless` | 无背景无边框 | 无边框 | 文本变为红色 |
### AddOn 扩展区域
ComboBox 支持四种扩展区域,均通过 `AddOnDecoratedBox` 实现:
| 区域 | 属性 | 位置 | 说明 |
|---|---|---|---|
| 前置标签 | `LeftAddOn` / `LeftAddOnTemplate` | 输入框外部左侧 | 带独立背景,常用于 `http://` 等前缀 |
| 后置标签 | `RightAddOn` / `RightAddOnTemplate` | 输入框外部右侧 | 带独立背景,常用于 `.com` 等后缀 |
| 内部前缀 | `ContentLeftAddOn` / `ContentLeftAddOnTemplate` | 内容区域左侧 | 与选中文本同行,常用于图标 |
| 内部后缀 | `ContentRightAddOn` / `ContentRightAddOnTemplate` | 内容区域右侧(手柄左侧) | 与选中文本同行,常用于辅助信息 |
### 下拉手柄(ComboBoxHandle)
ComboBox 内置一个 `ComboBoxHandle` 组件作为右侧内容附加件。手柄包含一个 `IconButton`(默认显示 `DownOutlined` 箭头图标),点击手柄可切换下拉面板的打开/关闭状态。手柄支持悬浮和按下时的颜色反馈。
### 下拉面板分页
`DropDownDisplayPageSize` 属性控制下拉面板最多可见的选项数量(默认 10 项)。AtomUI 会根据 `DropDownDisplayPageSize × ItemHeight + PopupContentPadding` 自动计算 `MaxDropDownHeight`,超出部分通过滚动条浏览。
### 验证状态(Status)
通过 `Status` 属性设置验证状态,对应的伪类(`:error`、`:warning`)会被激活。不同样式变体下的表现:
- **Outlined / Filled**:边框/背景颜色变为对应的错误/警告色系
- **Borderless**:选中文本的前景色变为对应的错误/警告色
### 表单集成
ComboBox 实现了 `IFormItemAware` 和 `IFormItemFeedbackAware` 接口:
- 当放置在 `FormItem` 中时,表单验证状态会自动映射为 `Status` 属性
- 验证反馈图标通过 `FormFeedback` 属性注入,显示在手柄左侧
---
## 与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
| 三种变体 `variant` | ✅ `outlined / filled / borderless` | ✅ `StyleVariant` 枚举 | ✅ 完全对齐 |
| 三种尺寸 `size` | ✅ `large / medium / small` | ✅ `SizeType` 属性 | ✅ 完全对齐 |
| 验证状态 `status` | ✅ `error / warning` | ✅ `Status` 属性 | ✅ 完全对齐 |
| 占位文本 `placeholder` | ✅ 字符串 | ✅ `PlaceholderText` 属性 | ✅ 完全对齐 |
| 前后附加 `addonBefore / addonAfter` | ✅ ReactNode | ✅ `LeftAddOn` / `RightAddOn` | ✅ 完全对齐 |
| 前后缀 `prefix / suffix` | ✅ ReactNode | ✅ `ContentLeftAddOn` / `ContentRightAddOn` | ✅ 完全对齐 |
| 允许清除 `allowClear` | ✅ 布尔 | ✅ `IsAllowClear` 属性 | ✅ 完全对齐 |
| 禁用 `disabled` | ✅ 布尔 | ✅ `IsEnabled` 属性 | ✅ 完全对齐 |
| 搜索 `showSearch` | ✅ 搜索过滤 | ❌ 暂未支持 | ⚠️ 待支持 |
| 多选 `mode="multiple"` | ✅ 多选/标签 | ❌ 暂未支持 | ⚠️ 待支持 |
| 分组 `optionGroup` | ✅ 选项分组 | ❌ 暂未支持 | ⚠️ 待支持 |
| 虚拟滚动 `virtual` | ✅ 默认开启 | ⚠️ 通过 Avalonia VirtualizingStackPanel 部分支持 | ⚠️ 部分对齐 |
| 下拉面板自定义 `dropdownRender` | ✅ 自定义渲染 | ❌ 暂未支持 | ⚠️ 待支持 |
| 远程搜索 | ✅ 异步加载 | ❌ 暂未支持 | ⚠️ 待支持 |
---
## 继承关系
```
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ItemsControl
└── Avalonia.Controls.SelectingItemsControl
└── Avalonia.Controls.ComboBox
└── AtomUI.Desktop.Controls.ComboBox
├── implements IMotionAwareControl
├── implements ISizeTypeAware
├── implements IInputControlStatusAware
├── implements IInputControlStyleVariantAware
├── implements IFormItemAware
└── implements IFormItemFeedbackAware
```
`ComboBox` 通过 `using AvaloniaComboBox = Avalonia.Controls.ComboBox;` 别名引用 Avalonia 原生 ComboBox,避免类名冲突。
> **注意**:ComboBox 没有对应的 `Abstract*` 基类(在 `AtomUI.Controls` 层没有抽象基类),这是因为 ComboBox 直接继承自 Avalonia 的 `ComboBox`,扩展逻辑完全在 `AtomUI.Desktop.Controls` 层实现。
**各层级职责划分:**
| 层级 | 提供的能力 |
|---|---|
| `ItemsControl` | 项目集合管理、`ItemsSource` 数据绑定、`ItemTemplate` 模板化、容器回收 |
| `SelectingItemsControl` | 单选管理、`SelectedItem` / `SelectedIndex`、`SelectionChanged` 事件 |
| `Avalonia.Controls.ComboBox` | 下拉弹出 `Popup`、`PlaceholderText` 占位提示、`MaxDropDownHeight`、`IsDropDownOpen`、键盘导航、`SelectionBoxItem` 显示选中内容 |
| `AtomUI.Desktop.Controls.ComboBox` | Ant Design 视觉体系(三种变体/三种尺寸/验证状态)、AddOn 扩展区域、Design Token 集成、下拉手柄、分页下拉高度、表单集成 |
**实现的共享接口:**
| 接口 | 定义位置 | 作用 |
|---|---|---|
| `IMotionAwareControl` | `AtomUI.Controls.Shared` | 支持 `IsMotionEnabled` 动画开关 |
| `ISizeTypeAware` | `AtomUI.Controls.Shared` | 支持 `SizeType`(Small / Middle / Large)三种尺寸切换 |
| `IInputControlStatusAware` | `AtomUI.Controls.Shared` | 支持 `Status`(Error / Warning / Default)验证状态 |
| `IInputControlStyleVariantAware` | `AtomUI.Controls.Shared` | 支持 `StyleVariant`(Outlined / Filled / Borderless)样式变体 |
| `IFormItemAware` | `AtomUI.Controls.Shared` | 可作为 `FormItem` 的子控件参与表单验证 |
| `IFormItemFeedbackAware` | `AtomUI.Controls.Shared` | 可接收并显示表单验证反馈控件 |
---
## 源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 控件类 | `src/AtomUI.Desktop.Controls/ComboBox/ComboBox.cs` | 桌面端 ComboBox 具体实现 |
| 控件类 | `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxHandle.cs` | 下拉手柄组件(内部) |
| 控件类 | `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxItem.cs` | 下拉选项容器 |
| 反射扩展 | `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxReflectionExtensions.cs` | Avalonia ComboBox 内部字段访问 |
| Token 定义 | `src/AtomUI.Desktop.Controls/ComboBox/ComboBoxToken.cs` | 组件级 Design Token |
| 主题模板 | `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxTheme.axaml` | ComboBox ControlTheme |
| 选项主题 | `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxItemTheme.axaml` | ComboBoxItem ControlTheme |
| 手柄主题 | `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxHandleTheme.axaml` | ComboBoxHandle ControlTheme |
| 主题注册 | `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxThemes.axaml` | ResourceDictionary 聚合注册 |
| 模板常量 | `src/AtomUI.Desktop.Controls/ComboBox/Themes/ComboBoxThemeConstants.cs` | 模板部件名称常量 |
| Gallery 示例 | `controlgallery/AtomUIGallery/ShowCases/Views/Navigation/ComboBoxShowCase.axaml` | 使用范例 |
---
## 模板结构
ComboBox 的 ControlTemplate 采用 Panel 布局,分为两个主要区域:**AddOn 装饰容器**和**弹出下拉面板**。
```
Panel
├── AddOnDecoratedBox (PART_AddOnDecoratedBox) ← 装饰容器(管理边框/背景/AddOn 区域)
│ ├── [LeftAddOn] ← 前置标签(外部左侧)
│ ├── [ContentLeftAddOn] ← 内部前缀(内容左侧)
│ ├── Panel ← 内容区域
│ │ ├── TextBlock#PlaceholderText ← 占位提示文本(未选中时显示)
│ │ └── ContentPresenter#SelectedContentPresenter ← 选中内容展示器
│ ├── StackPanel (ContentRightAddOn) ← 内部后缀区域
│ │ ├── ContentPresenter (ContentRightAddOn) ← 用户自定义后缀
│ │ ├── ContentPresenter#FormFeedBack ← 表单验证反馈图标
│ │ └── ComboBoxHandle ← 下拉箭头手柄
│ └── [RightAddOn] ← 后置标签(外部右侧)
└── Popup (PART_Popup) ← 弹出下拉面板
└── Border#PopupFrame ← 弹出框架(背景/圆角/阴影)
└── ScrollViewer ← 滚动容器
└── ItemsPresenter (PART_ItemsPresenter) ← 选项列表
```
**分层设计理由:**
- **AddOnDecoratedBox 统一管理**:所有输入型控件(TextBox、ComboBox、ButtonSpinner 等)共用 `AddOnDecoratedBox` 作为外层装饰,确保样式变体(Outlined/Filled/Borderless)、验证状态、AddOn 区域的一致表现。
- **Popup 独立**:弹出面板使用 `atom:Popup`(AtomUI 增强版),支持动画展开/收起(`IsMotionAwareOpen`)和自动阴影。
- **手柄内置**:`ComboBoxHandle` 作为固定的内部后缀元素,始终显示在最右侧,确保下拉箭头的交互一致性。
### 模板部件常量
| 常量名 | 值 | 说明 |
|---|---|---|
| `ComboBoxThemeConstants.PopupPart` | `"PART_Popup"` | 弹出下拉面板 |
| `ComboBoxThemeConstants.SpinnerInnerBoxPart` | `"PART_SpinnerInnerBox"` | 内部输入框(预留) |
| `ComboBoxThemeConstants.ItemsPresenterPart` | `"PART_ItemsPresenter"` | 选项列表展示器 |
| `ComboBoxHandleThemeConstants.OpenIndicatorButtonPart` | `"PART_OpenIndicatorButton"` | 下拉指示器按钮 |
### ComboBoxItem 模板结构
```
ContentPresenter#ContentPresenter ← 选项内容展示器
Content → StringToTextBlockConverter ← 字符串自动转 TextBlock
Background / Padding / CornerRadius ← 由 Token 控制
Margin ← 由 ComboBoxToken.ItemMargin 控制
```
### ComboBoxHandle 模板结构
```
IconButton (PART_OpenIndicatorButton) ← 下拉指示器按钮
Icon = DownOutlined ← Ant Design 向下箭头图标
```