15 KiB
TreeSelect 桌面版架构设计
本文档定义 AtomUI.Desktop.Controls.TreeSelect 的最新设计定位、公共契约、状态模型、视觉主题关系和兼容边界。共享输入分层见 输入控件共享架构设计,通用控件研发约束见 控件研发标准,内部实现原理见 TreeSelect 桌面版实现原理,TreeSelect Token 的专项设计见 TreeSelect Token 设计,设计和契约变化记录见 TreeSelect Changelog。
该控件的 Popup 钉住打开属于共享弹层契约,详见 Popup 钉住打开设计。本控件的语义 owner 为 AbstractSelect,其 internal IsPopupPinnedOpen 只供测试和内部诊断使用;设置为 true 时保持 IsDropDownOpen 并 relay 到 PART_Popup,设置为 false 时只解除关闭拦截。控件卸载、锚点失效、TopLevel 改变和模板重建仍按共享生命周期规则清理。
1. 控件定位
| 项 | 值 |
|---|---|
| NuGet 包 | AtomUI.Desktop.Controls |
| .NET 命名空间 | AtomUI.Desktop.Controls |
| AXAML 命名空间 | https://atomui.net |
| Gallery 页面 | controlgallery/AtomUIGallery/ShowCases/DataEntry/TreeSelect |
| 控件状态 | Stable |
TreeSelect 是 AtomUI 桌面数据录入体系中的树形选择控件,用于在层级数据中完成单选、多选和勾选式选择。它组合 Select 家族的输入壳体、过滤输入、弹层和 Form / CompactSpace 集成能力,并使用 TreeView 家族承载树节点、展开、勾选、图标、连线和异步加载。
TreeSelect 的职责是从 ITreeItemNode 层级数据中选择一个或多个节点。它不负责远程数据协议、业务权限过滤、级联业务规则、树节点编辑、拖放排序或复杂数据持久化;这些能力应由业务层、TreeView 或专用数据控件承担。
2. 设计语言
TreeSelect 的设计语言来自输入框、树形候选弹层和已选结果的组合。
| 维度 | 含义 | 典型表达 |
|---|---|---|
| 树形数据 | 候选项按父子层级展示。 | ItemsSource、Items、ItemTemplate。 |
| 选择模式 | 控件支持单选、多选和 checkbox 勾选。 | IsMultiple、IsTreeCheckable、IsTreeCheckStrictly。 |
| 结果展示 | 单选显示节点文本,多选以 tag 展示有效选中节点。 | SelectedItem、SelectedItems、ShowCheckedStrategy。 |
| 搜索过滤 | 输入过滤值后高亮、加粗、展开路径或隐藏不匹配节点。 | IsFilterEnabled、FilterValue、Filter、FilterStrategy。 |
| 树视觉 | 控制图标、叶子图标、连线、展开指示和旋转。 | IsShowIcon、IsShowLeafIcon、IsShowLine、IsShowTreeLine。 |
| 输入表面 | 继承 Select 家族的边框、尺寸、状态和 AddOn。 | SizeType、StyleVariant、Status、Addon。 |
| 弹层语义 | 候选树在 popup 中打开,可匹配输入宽度。 | IsDropDownOpen、PopupPlacement、IsPopupMatchSelectWidth。 |
SizeType=Custom 不是 TreeSelect 的第四套专属 Token。主题层把 Custom 归入 Middle 的默认字体和 padding 分支;用户显式设置高度等尺寸属性时由 Avalonia 属性优先级决定最终布局。
3. API 与契约模型
TreeSelect 继承 AbstractSelect 的输入壳体、弹层、清除、状态、尺寸、Addon、Form 和 CompactSpace 契约;自身提供树数据、树选择、过滤和勾选策略契约。
树数据 API:
| API | 类型 | 语义 |
|---|---|---|
ItemsSource |
IEnumerable<ITreeItemNode>? |
外部树节点集合。变化时同步到内部 Items。 |
Items |
ItemCollection |
XAML 内容子项入口,也是候选树实际数据入口。 |
ItemTemplate |
IDataTemplate? |
树节点显示模板,默认显示 ITreeItemNode.Header。 |
DataLoader |
ITreeItemNodeLoader? |
展开节点时加载子节点的数据加载边界。 |
TreeDefaultExpandedPaths |
IList<TreeNodePath>? |
默认展开路径集合。 |
IsDefaultExpandAll |
bool |
默认展开全部节点。 |
选择 API:
| API | 类型 | 语义 |
|---|---|---|
SelectedItem |
ITreeItemNode? |
单选模式当前节点;默认 BindingMode.TwoWay,启用 Avalonia DataValidationErrors。 |
SelectedItems |
IList<ITreeItemNode>? |
多选或勾选模式当前节点集合;默认 BindingMode.TwoWay,启用 Avalonia DataValidationErrors,支持 INotifyCollectionChanged 原地变更刷新。 |
IsMultiple |
bool |
是否使用多选结果模型。 |
IsTreeCheckable |
bool |
是否使用 checkbox 勾选。启用后 TreeSelect 使用多选结果模型。 |
IsTreeCheckStrictly |
bool |
勾选时父子节点是否严格独立。 |
ShowCheckedStrategy |
TreeSelectCheckedStrategy |
多选 tag 的展示策略,决定展示全部、父节点或叶子节点。 |
MaxCount |
int |
继承自 AbstractSelect 的最大可选数量。 |
Clear() |
method | 清空 SelectedItem 和 SelectedItems。 |
过滤与树视觉 API:
| API | 类型 | 语义 |
|---|---|---|
Filter |
IValueFilter? |
树节点过滤器;为空时初始化为 contains 过滤。 |
FilterValueSelector |
DefaultFilterValueSelector? |
从节点中提取过滤文本。 |
FilterStrategy |
TreeFilterStrategy |
过滤后的高亮、加粗、展开路径和隐藏不匹配策略。 |
FilterHighlightForeground |
IBrush? |
过滤命中高亮前景色。 |
AutoScrollToSelectedItem |
bool |
候选树打开或同步时滚动到选中节点。 |
IsShowOverflowTip |
bool |
单选结果文本或多选 tag 视觉溢出时是否显示完整内容 tooltip,默认 true。 |
OverflowTipDelay |
int |
溢出 tooltip 打开前的延迟时间,单位毫秒,默认 1200。 |
OverflowTipPlacement |
PlacementMode |
溢出 tooltip 相对单选结果文本或多选 tag 的位置,默认 TopEdgeAlignedLeft。 |
IsShowIcon / IsShowLeafIcon |
bool |
是否显示节点图标和叶子节点图标。 |
IsShowLine / IsShowTreeLine |
bool |
是否显示层级连线。 |
IsSwitcherRotation |
bool |
展开图标是否旋转。 |
稳定 template part:
| Template Part | 类型 | 职责 |
|---|---|---|
PART_InputControlFrame |
TreeSelectAddOnDecoratedBox |
InputControlFrame / AddOnDecoratedBox 组合、Addon、EffectiveStatus、CompactSpace 和多选布局承载。 |
PART_SingleFilterInput |
SelectFilterTextBox |
单选模式结果显示和搜索输入。 |
SelectedItemsBox |
SelectTagAwareTextBox |
多选和勾选模式已选 tag 展示。 |
PART_SelectMaxCountIndicator |
SelectMaxCountIndicator |
最大选择数量提示。 |
PART_ContentRightAddOnPresenter |
ContentPresenter |
用户内部右侧内容承载。 |
PART_SelectHandle |
SelectHandle |
展开、loading、清除和 Form feedback 图标入口。 |
PART_Popup |
Popup |
候选弹层宿主。 |
PopupFrame |
Border |
懒创建的候选弹层外壳。 |
PART_TreeView |
TreeSelectTreeView |
懒创建的树候选控件。 |
4. 行为与状态模型
TreeSelect 的核心状态流:
ItemsSource / Items
↓
TreeSelectTreeView
↓
SelectedItem / SelectedItems
↓
SingleFilterInput / SelectedItemsBox
↓
Form value + MaxCount state
选择模式语义:
- 单选模式使用
SelectedItem作为表单值,候选节点点击后关闭弹层。 - 多选模式使用
SelectedItems作为表单值,候选树使用多选 selection,并通过 tag 展示结果。 IsTreeCheckable=true使用 checkbox 作为节点切换入口,TreeView selection 不再作为主要选择入口。ShowCheckedStrategy只影响多选 tag 展示集合,不改变SelectedItems的真实值。SelectedItems是用户拥有的受控集合。集合引用替换和ObservableCollection等INotifyCollectionChanged原地Add、Remove、Reset都必须刷新 tag、SelectedCount、Form value changed、候选树 selection / checked items 和最大选择数状态。
过滤行为:
FilterValue来自单选搜索输入。FilterStrategy控制命中高亮、加粗、展开路径和隐藏不匹配节点。- 弹层关闭后,单选模式清空过滤值并恢复结果显示。
清除行为通过 SelectHandle.ClearRequestedEvent 冒泡到 TreeSelect,并调用 Clear() 清空当前选择。
5. 视觉与主题模型
TreeSelect 的默认视觉由 TreeSelect 专属主题、InputControlFrame / Select 家族 AddOnDecoratedBox、TreeView 和 PopupHost 协作完成。
| 主题或资源 | 职责 |
|---|---|
TreeSelectTheme.axaml |
根模板、输入壳体、单选输入、多选结果区域、handle、popup 和基础 selector。 |
SelectTagAwareTextBox / SelectTag 主题 |
多选 tag 布局、高度、关闭按钮和禁用态。 |
TreeView / TreeViewItem 主题 |
树节点缩进、展开、勾选、图标、连线和过滤视觉。 |
InputControlFrameTheme / SharedToken |
输入壳体边框、圆角、effective status、focus 和 CompactSpace 视觉。 |
PopupHostToken |
popup 阴影、圆角和 anchor margin。 |
TreeSelectToken |
TreeSelect 候选弹层最小宽度。 |
SelectToken |
TreeSelect 复用的 popup padding、多选 tag 和输入内容 padding。 |
单选结果文本和多选 tag 的完整内容提示复用共享 OverflowTip attached behavior。主题通过 IsShowOverflowTip、OverflowTipDelay 和 OverflowTipPlacement 控制提示开关、延迟和位置,实际 tooltip 仅在文本视觉溢出时托管到 ToolTip。
右侧 count、content add-on 和 handle 的稳定 template part 状态由 AXAML compiled ancestor binding 表达。C# 中只保留 frame layout part 到 SelectHandle 的 hover / pressed sibling 状态转发,因为该关系不是 templated parent 绑定,不能用 TemplateBinding 表达;该转发不改变 InputControlFrame 对输入表面状态的唯一 ownership。
6. 控件家族或集成关系
TreeSelect 属于 Data Entry 选择控件家族,与 Select、Cascader、DatePicker、TimePicker 等共享输入尺寸、variant、status、Addon、Form 和 CompactSpace 语义。
集成关系:
AbstractSelect:Select 家族输入壳体、弹层、Form、CompactSpace、Motion 和状态契约。TreeView:树节点容器、展开、勾选、过滤和异步加载基础能力。TreeSelectTreeView:TreeSelect 专用 TreeView,负责最大选择数状态下的节点容器协作。TreeViewSelectTreeViewItem:候选树容器,接收最大选择数状态。SelectHandle:右侧操作入口,负责展开指示、loading、清除和 Form feedback。IFormItemAware/IFormItemFeedbackAware:将选择值、Form 扩展状态和 feedback 接入 Form;error 由DataValidationErrors投射到InputControlFrame。ICustomizableSizeTypeAware:接入支持Custom的输入尺寸模型。
7. 兼容性不变量
维护 TreeSelect 时必须保持以下不变量:
- 单选模式使用
SelectedItem,多选和勾选模式使用SelectedItems。 SelectedItem和SelectedItems必须保持默认双向绑定,并通过 AvaloniaDataValidationErrors承接 binding / Form error。SelectedItems原地变更必须与集合替换走同一套展示、Form 和候选树同步路径,不能依赖用户重新赋值。IsTreeCheckable=true必须继续把 TreeSelect 归入多选结果模型。ShowCheckedStrategy只能影响EffectiveSelectedItems,不能改写真实SelectedItems。ItemsSource变化必须尽量按节点路径 identity 保留已有选择。Clear()必须同时清空单选和多选状态。- Popup 打开、关闭和 light-dismiss 语义必须继续由
AbstractSelect管理。 - 重新套用模板或 detach 时必须释放旧 popup 内容、TreeView 事件订阅和模板 part relay binding。
- 右侧稳定 template part 优先使用 AXAML binding,不能把可静态表达的绑定重新搬回 C#。
SizeType=Custom必须继续以Middle作为未显式覆盖时的默认视觉基线。- Template part、Token 名称、伪类和主题 selector 不得在未授权情况下重命名或删除。
8. 专项模型
8.1 勾选展示策略
TreeSelectCheckedStrategy 是多选结果展示策略:
All:展示所有已选节点。ShowParent:当父节点下所有子节点都被选中时,结果展示父节点,隐藏对应子孙节点。ShowChild:只展示叶子节点。
该策略作用于内部 EffectiveSelectedItems,最终由 SelectedItemsBox 展示,不改变 SelectedItems 的表单值。
8.2 路径 identity
TreeSelect 使用节点的 ItemKey 或 Value 组成 TreeNodePath,用于 ItemsSource 替换后的选择重映射。节点缺少稳定 identity 时,控件无法可靠地跨数据源替换保留选择。
9. 文档导航、LLMS 导出与验证策略
验证要求:
| 改动类型 | 验证要求 |
|---|---|
| Public API 或 Form 映射 | 验证单选、多选、勾选和 Clear() 行为。 |
| 选择同步 | 验证 TreeView selection / checked items 与 SelectedItem / SelectedItems 双向同步,并覆盖 SelectedItems 集合替换和原地变更。 |
| 过滤行为 | 验证单选搜索输入、过滤策略、弹层关闭恢复和 placeholder 状态。 |
| AXAML 或 template part | 验证右侧 AddOn/count/handle binding、hover/pressed relay、popup 内容和 Gallery 示例。 |
| Token 或尺寸 | 验证 Large / Middle / Small / Custom、tag 高度、popup padding 和最小宽度。 |
| 文档改动 | 运行 git diff --check,检查文档链接存在。 |
LLMS 语义区域:
| Part | AtomUI 节点 | 职责 | 相关 API | 相关 Token | 稳定性 |
|---|---|---|---|---|---|
root |
TreeSelect |
数据录入控件根语义区域,承载 public API、值状态、验证状态和主题入口。 | 见 API 与契约模型 | 见视觉与主题模型 | stable |
input |
输入或编辑区域 |
承载用户输入、当前值、占位、格式化或只读状态。 | 见 API 与契约模型 | 见视觉与主题模型 | stable |
trigger |
触发区域 |
承载清除、展开、提交、步进、上传或辅助操作。 | 见 API 与契约模型 | 见视觉与主题模型 | stable |
popup |
弹层或候选区域 |
承载下拉、候选项、日历、颜色面板或异步内容。 | 见 API 与契约模型 | 见视觉与主题模型 | stable |
validation |
校验反馈区域 |
承载 Form、status、错误、警告、help 或 loading 状态。 | 见 API 与契约模型 | 见视觉与主题模型 | stable |
LLMS 导出来源:
| LLMS 内容 | 来源 | 说明 |
|---|---|---|
| 单控件完整文档 | overview.md + implementation.md + token.md + Gallery ShowCase |
生成 controls/tree-select/index-cn.md |
| 单控件语义文档 | overview.md + implementation.md + theme/template 信息 |
生成 controls/tree-select/semantic-cn.md |
| API 表 | overview.md 语义摘要 + 源码 public surface | 不在 overview.md 中复制完整 API 表 |
| Design Token 表 | token.md、Token 类型或第 5 节主题模型 | 不在生成产物中手工维护第二份 Token 表 |
| 示例 | Gallery ShowCase + source snippet catalog | 只引用稳定示例 |
| 源码索引 | implementation.md |
用于定位控件源码、主题和测试 |