Files

16 KiB
Raw Permalink Blame History

NavMenu 导航菜单

概述

导航菜单(NavMenu)是网站和应用中最常见的导航交互控件之一,用于为用户提供清晰的页面导航路径。它支持水平(Horizontal)、垂直(Vertical)和内嵌(Inline)三种展示模式,并可在亮色和暗色主题之间切换。

AtomUI 的 NavMenu 控件对标 Ant Design 5.0 Menu(导航菜单) 的设计规范,在 .NET / Avalonia 平台上提供一致的视觉体验和交互行为。


设计原理

Ant Design 的导航菜单设计哲学

Ant Design 导航菜单的核心定位是:为页面和功能提供导航的菜单列表,帮助用户在不同层级的内容之间快速跳转。菜单支持多级层级结构,用户可以展开/收起子菜单来浏览不同层级的导航项。

三种菜单模式(按场景区分):

模式 设计意图 典型场景
🔹 水平模式(Horizontal) 水平排列的一级菜单项,子菜单通过弹出层展示。适合顶部导航栏 网站/应用的顶部全局导航
🔸 垂直模式(Vertical) 垂直排列的菜单项,子菜单通过弹出层(Popup)在侧边展示 侧边栏导航(弹出式子菜单)
🔻 内嵌模式(Inline) 垂直排列的菜单项,子菜单在当前面板内展开/折叠,具有展开/收起动画 侧边栏导航(折叠式子菜单)

关键交互特性:

特性 说明
选中高亮 当前选中的菜单项高亮显示,祖先路径上的父级菜单项也会标记为「选中路径」
手风琴模式 同级菜单项中只能有一个展开,展开新子菜单时自动关闭其他已展开的兄弟菜单
默认展开/选中 支持通过路径指定初始展开的子菜单和默认选中的菜单项
暗色主题 支持独立的暗色主题样式,常用于深色背景的侧边导航栏
图标支持 菜单项可配置图标,提升可识别性
禁用状态 单个菜单项可设为禁用,不响应交互

与 Avalonia 的关系

AtomUI 的 NavMenu 不继承自 Avalonia 内置的 Menu 控件,而是直接继承自 ItemsControl。这是因为 Ant Design 的导航菜单与传统的应用程序菜单(File/Edit/View 等)有本质区别——它是一个页面级的导航组件,需要支持选中状态跟踪、路径高亮、内嵌展开动画等 Avalonia Menu 不具备的功能。

NavMenu 使用自定义的 NavMenuItem(继承自 HeaderedSelectingItemsControl)作为菜单项容器,实现了完整的自定义交互逻辑。

AtomUI NavMenu 的核心设计

设计要素 实现方式 设计动机
三种菜单模式 NavMenuMode 枚举 + 伪类驱动 + 交互处理器切换 不同模式对应不同的布局策略和子菜单展示方式
交互处理器策略 INavMenuInteractionHandler 接口 + DefaultNavMenuInteractionHandler / InlineNavMenuInteractionHandler 将交互逻辑从控件中解耦,不同模式使用不同的处理器
树形数据模型 INavMenuNode / NavMenuNode + ITreeNode<T> 支持层级数据结构,每个节点可包含子节点
选中路径追踪 IsInSelectedPath 属性 + CollectSelectPathItems 从选中项向上追溯到根节点,标记整条路径上的祖先菜单项
默认展开/选中 DefaultOpenPaths / DefaultSelectedPath + TreeNodePath 通过路径字符串指定初始状态
手风琴模式 IsAccordionMode 属性 + NotifySubmenuOpened 互斥逻辑 同级只允许一个子菜单展开
亮色/暗色主题 IsDarkStyle 属性 + :dark / :light 伪类 通过 Token 系统提供完整的暗色主题
展开/折叠动画 Inline 模式下使用 SlideUpInMotion / SlideUpOutMotion 平滑的子菜单展开/收起效果
Design Token NavMenuToken + 全局 SharedToken 所有视觉属性从 Token 派生,支持主题定制

功能详解

菜单模式(NavMenuMode)

水平模式(Horizontal)

菜单项水平排列成一行,通常用于页面顶部导航。子菜单通过 Popup 弹出层在菜单项下方展示。水平模式下选中项有底部指示条效果。

垂直模式(Vertical)

菜单项垂直排列,子菜单通过 Popup 弹出层在菜单项右侧展示。鼠标悬浮时延迟展开子菜单,移出后延迟关闭。

内嵌模式(Inline)

菜单项垂直排列,子菜单在当前面板内通过折叠/展开方式显示。点击有子菜单的菜单项会触发展开/收起动画(SlideUpInMotion / SlideUpOutMotion)。

模式与交互处理器的对应关系:

模式 交互处理器 子菜单展示方式
Horizontal DefaultNavMenuInteractionHandler Popup 弹出层
Vertical DefaultNavMenuInteractionHandler Popup 弹出层
Inline InlineNavMenuInteractionHandler 就地展开/折叠

选中路径追踪

当用户选中一个叶子菜单项时,NavMenu 会:

  1. 清除之前选中项及其祖先路径上的 IsInSelectedPath 标记
  2. 为新选中项的所有祖先菜单项设置 IsInSelectedPath = true
  3. 触发 NavMenuNodeSelected 事件,更新 SelectedItem 属性

这使得主题可以为整条选中路径上的菜单项应用高亮样式。

手风琴模式(Accordion)

当 IsAccordionMode = true 时,同一级别中只能有一个子菜单处于展开状态。当一个子菜单被展开时,其兄弟子菜单会自动关闭。

默认展开与默认选中

  • DefaultOpenPaths:通过 TreeNodePath 列表指定初始展开的路径。路径格式为 /Key1/Key2/Key3,由各节点的 ItemKey 拼接而成。
  • DefaultSelectedPath:指定初始选中的菜单项路径。
  • SelectedItem:直接设置选中的 INavMenuNode 对象,优先级高于 DefaultSelectedPath。

亮色/暗色主题

通过 IsDarkStyle 属性切换菜单的视觉主题。暗色模式下使用一套独立的 Token(如 DarkMenuBg、DarkItemColor、DarkItemSelectedBg 等),提供深色背景下的完整样式支持。


与 Ant Design 规范的对齐程度

特性 Ant Design (React) AtomUI (Avalonia) 对齐情况
三种模式 mode ✅ vertical / horizontal / inline ✅ NavMenuMode 枚举 ✅ 完全对齐
暗色主题 theme ✅ light / dark ✅ IsDarkStyle 属性 ✅ 完全对齐
选中项 selectedKeys ✅ 数组 ✅ SelectedItem 单选 ⚠️ 仅支持单选
默认展开 defaultOpenKeys ✅ 数组 ✅ DefaultOpenPaths 路径列表 ✅ 对齐(路径格式不同)
默认选中 defaultSelectedKeys ✅ 数组 ✅ DefaultSelectedPath 单路径 ⚠️ 仅支持单选
手风琴模式 ✅ openKeys 受控 ✅ IsAccordionMode 属性 ✅ 对齐
图标支持 icon ✅ ReactNode ✅ PathIcon ✅ 对齐
禁用项 disabled ✅ 布尔 ✅ IsEnabled ✅ 完全对齐
菜单项分组 ItemGroup ✅ 支持 ❌ 暂未支持 ⚠️ 待实现
菜单分割线 Divider ✅ 支持 ❌ 暂未支持 ⚠️ 待实现
折叠收起 inlineCollapsed ✅ 支持 ❌ 暂未支持 ⚠️ 待实现
点击事件 onClick ✅ 回调 ✅ NavMenuItemClick 事件 ✅ 完全对齐
选中事件 onSelect ✅ 回调 ✅ NavMenuNodeSelected 事件 ✅ 完全对齐
items 配置式 ✅ JSON 数组 ✅ NavMenuNode 数据模型 ✅ 对齐(形式不同)

继承关系

NavMenu

Avalonia.Controls.Primitives.TemplatedControl
  └── Avalonia.Controls.ItemsControl
        └── AtomUI.Desktop.Controls.NavMenu
              ├── implements IFocusScope
              ├── implements INavMenu
              ├── implements IMotionAwareControl
              └── implements IMenuChildSelectable (internal)

NavMenuItem

Avalonia.Controls.Primitives.TemplatedControl
  └── Avalonia.Controls.ItemsControl
        └── Avalonia.Controls.Primitives.SelectingItemsControl
              └── Avalonia.Controls.Primitives.HeaderedSelectingItemsControl
                    └── AtomUI.Desktop.Controls.NavMenuItem (internal)
                          ├── implements INavMenuItem
                          ├── implements ISelectable
                          ├── implements ICommandSource
                          ├── implements IClickableControl
                          ├── implements ICustomHitTest
                          └── implements IMenuChildSelectable (internal)

NavMenuNode(数据模型)

Avalonia.AvaloniaObject
  └── AtomUI.Desktop.Controls.NavMenuNode
        └── implements INavMenuNode
              └── extends ITreeNode<INavMenuNode>

各层级职责划分:

层级 提供的能力
ItemsControl 子项集合管理、容器化、ItemsSource 绑定、ItemTemplate 模板
NavMenu 三种模式切换、交互处理器调度、选中路径追踪、默认展开/选中、手风琴模式、亮色/暗色主题、Design Token 集成
NavMenuItem 单个菜单项行为:子菜单展开/关闭、命令绑定、选中状态、Popup 管理、内嵌展开动画、层级计算
NavMenuNode 数据模型:Header、Icon、ItemKey、子节点集合、父节点引用

核心类型

接口体系

接口 定义位置 职责
INavMenu AtomUI.Desktop.Controls NavMenu 根控件接口,提供 SelectedItem、DefaultOpenPaths、Close 等能力
INavMenuElement AtomUI.Desktop.Controls 菜单元素基接口,提供 SubItems 子项枚举
INavMenuItem AtomUI.Desktop.Controls 菜单项接口,定义 HasSubMenu、IsSubMenuOpen、Open/Close 等行为
INavMenuNode AtomUI.Desktop.Controls 菜单节点数据接口,继承自 ITreeNode<INavMenuNode>
INavMenuInteractionHandler AtomUI.Desktop.Controls (internal) 交互处理器接口,策略模式
IMenuChildSelectable AtomUI.Desktop.Controls (internal) 子项选中管理接口

辅助类型

类型 说明
NavMenuNode INavMenuNode 的默认实现,可直接在 AXAML 中声明使用
NavNodeKey 菜单节点键值结构体,支持字符串比较
TreeNodePath 树节点路径,格式为 /Key1/Key2,用于指定默认展开/选中路径
NavMenuItemClickEventArgs 菜单项点击事件参数
NavMenuNodeSelectedEventArgs 菜单节点选中事件参数
BaseNavMenuItemHeader 菜单项头部基类,承载 Header、Icon、动画过渡等通用逻辑
HorizontalNavMenuItemHeader 水平模式专用的菜单项头部
VerticalNavMenuItemHeader 垂直模式专用的菜单项头部
InlineNavMenuItemHeader 内嵌模式专用的菜单项头部

源码位置

层级 文件路径 说明
控件类 src/AtomUI.Desktop.Controls/NavMenu/NavMenu.cs NavMenu 主控件
菜单项 src/AtomUI.Desktop.Controls/NavMenu/NavMenuItem.cs NavMenuItem 菜单项容器(internal)
数据模型 src/AtomUI.Desktop.Controls/NavMenu/NavMenuNode.cs NavMenuNode 数据模型 + INavMenuNode 接口
Token 定义 src/AtomUI.Desktop.Controls/NavMenu/NavMenuToken.cs 组件级 Design Token
伪类常量 src/AtomUI.Desktop.Controls/NavMenu/NavMenuPseudoClass.cs 伪类定义
模式枚举 src/AtomUI.Desktop.Controls/NavMenu/NavMenuMode.cs NavMenuMode 枚举
事件参数 src/AtomUI.Desktop.Controls/NavMenu/NavMenuEventArgs.cs 事件参数类
接口定义 src/AtomUI.Desktop.Controls/NavMenu/INavMenu.cs INavMenu 接口
接口定义 src/AtomUI.Desktop.Controls/NavMenu/INavMenuElement.cs INavMenuElement 接口
接口定义 src/AtomUI.Desktop.Controls/NavMenu/INavMenuItem.cs INavMenuItem 接口
键结构体 src/AtomUI.Desktop.Controls/NavMenu/NavNodeKey.cs NavNodeKey 结构体
交互处理器 src/AtomUI.Desktop.Controls/NavMenu/DefaultNavMenuInteractionHandler.cs 默认(Vertical/Horizontal)交互处理器
交互处理器 src/AtomUI.Desktop.Controls/NavMenu/InlineNavMenuInteractionHandler.cs 内嵌模式交互处理器
头部控件 src/AtomUI.Desktop.Controls/NavMenu/Header/BaseNavMenuItemHeader.cs 菜单项头部基类
头部控件 src/AtomUI.Desktop.Controls/NavMenu/Header/HorizontalNavMenuItemHeader.cs 水平模式头部
头部控件 src/AtomUI.Desktop.Controls/NavMenu/Header/VerticalNavMenuItemHeader.cs 垂直模式头部
头部控件 src/AtomUI.Desktop.Controls/NavMenu/Header/InlineNavMenuItemHeader.cs 内嵌模式头部
主题模板 src/AtomUI.Desktop.Controls/NavMenu/Themes/NavMenuTheme.axaml NavMenu ControlTheme
主题模板 src/AtomUI.Desktop.Controls/NavMenu/Themes/NavMenuItemTheme.axaml NavMenuItem ControlTheme
主题模板 src/AtomUI.Desktop.Controls/NavMenu/Themes/BaseNavMenuItemHeaderTheme.axaml 基础头部主题
主题模板 src/AtomUI.Desktop.Controls/NavMenu/Themes/HorizontalNavMenuItemHeaderTheme.axaml 水平头部主题
主题模板 src/AtomUI.Desktop.Controls/NavMenu/Themes/VerticalNavMenuItemHeaderTheme.axaml 垂直头部主题
主题模板 src/AtomUI.Desktop.Controls/NavMenu/Themes/InlineNavMenuItemHeaderTheme.axaml 内嵌头部主题
模板常量 src/AtomUI.Desktop.Controls/NavMenu/Themes/NavMenuThemeConstants.cs 模板部件名称常量
工具类 src/AtomUI.Desktop.Controls/NavMenu/Utils/MarginMultiplierConverter.cs 缩进边距倍数转换器
工具类 src/AtomUI.Desktop.Controls/NavMenu/Utils/NavMenuIsSubMenuOpenConverter.cs 子菜单展开状态转换器
Gallery 示例 controlgallery/AtomUIGallery/ShowCases/Views/Navigation/MenuShowCase.axaml 使用范例
Gallery 代码 controlgallery/AtomUIGallery/ShowCases/Views/Navigation/MenuShowCase.axaml.cs 示例 code-behind

模板结构

NavMenu 模板

NavMenu 自身模板比较简单,主要是一个带 ItemsPresenter 的容器:

  • 水平模式:ItemsPanel 使用水平布局的 StackPanel,底部有一条指示线
  • 垂直/内嵌模式:ItemsPanel 使用垂直布局的 StackPanel

NavMenuItem 模板

NavMenuItem 的模板根据模式不同有较大差异:

  • 水平/垂直模式:使用 Popup 弹出子菜单
  • 内嵌模式:使用 BaseMotionActor(PART_ChildItemsLayoutTransform)包裹子菜单 ItemsPresenter,通过 SlideUpInMotion / SlideUpOutMotion 实现展开/收起动画

每种模式都有对应的 NavMenuItemHeader 控件作为菜单项头部区域。

模板部件常量

常量名 值 说明
NavMenuItemThemeConstants.HeaderPart "PART_Header" 菜单项头部区域
NavMenuItemThemeConstants.ItemsPresenterPart "PART_ItemsPresenter" 子项展示器
NavMenuItemThemeConstants.PopupFramePart "PART_PopupFrame" Popup 框架
NavMenuItemThemeConstants.PopupPart "PART_Popup" Popup 弹出层
InlineNavMenuItemThemeConstants.ChildItemsLayoutTransformPart "PART_ChildItemsLayoutTransform" 内嵌模式子项动画容器
NavMenuThemeConstants.ItemsPresenterPart "PART_ItemsPresenter" NavMenu 子项展示器
NavMenuThemeConstants.HorizontalLinePart "PART_HorizontalLine" 水平模式底部指示线
TopLevelHorizontalNavMenuItemThemeConstants.ActiveIndicatorPart "PART_ActiveIndicator" 水平顶层菜单项的活跃指示器