12 KiB
Dialog 对话框
概述
对话框(Dialog)是一个从界面中弹出的模态或非模态面板,用于承载需要用户关注或交互的内容。典型使用场景包括:确认操作、表单填写、详情展示、流程引导等。对话框在出现时会在视觉上「聚焦」用户注意力,阻止(模态)或不阻止(非模态)用户与底层界面的交互。
AtomUI 的 Dialog 控件对应 Ant Design 的 Modal 组件。在 AtomUI 中采用 Dialog 命名(而非 Modal),是因为 Dialog 更准确地描述了桌面端的语义——桌面应用中「对话框」是长期沿用的标准术语,且 AtomUI 的实现不仅支持模态(Modal),还支持非模态(Modeless)模式。
设计原理
Ant Design 的对话框设计哲学
Ant Design 对 Modal 的定位是:在当前页面打开一个浮层,承载相关操作或信息。用户不需要离开当前页面就能完成任务或获取信息。
典型使用场景:
| 场景 | 说明 |
|---|---|
| 🔔 信息确认 | 操作前的二次确认(如删除、提交),避免误操作 |
| 📝 表单填写 | 在不离开当前页面的情况下完成新建/编辑等操作 |
| 📄 详情展示 | 展示详细信息或预览内容 |
| ⚠️ 消息提示 | 成功、错误、警告、确认等快捷提示(MessageBox) |
AtomUI 的扩展设计
AtomUI Dialog 在 Ant Design Modal 基础上做了大量桌面端增强,核心设计决策包括:
双宿主架构(Dual Host):
| 宿主类型 | 说明 | 适用场景 |
|---|---|---|
Overlay |
基于 Overlay 层渲染,在当前窗口内覆盖显示 | 大多数场景(默认);对话框内容与主窗口共享渲染上下文 |
Window |
创建独立的原生窗口作为宿主 | 需要独立标题栏、系统窗口管理器交互、最小化/最大化等 |
标准按钮系统(Standard Buttons):
Dialog 提供了一套完整的标准按钮系统,基于 DialogStandardButton 枚举(Flags),支持 Ok、Cancel、Save、Yes、No、Apply、Reset 等 18 种标准按钮。每个标准按钮都有预定义的 DialogButtonRole(AcceptRole / RejectRole / DestructiveRole 等),Dialog 根据角色自动决定点击后的行为(Accept / Reject / Done)。
定位系统(Positioning):
Dialog 提供了灵活的定位能力:
HorizontalStartupLocation/VerticalStartupLocation:锚点定位(Left / Center / Right / Top / Bottom / Custom)HorizontalOffset/VerticalOffset:偏移量,支持像素和百分比(Dimension类型)PlacementTarget:定位参考控件PlacementCallback:完全自定义定位回调
MVVM 集成:
通过 IDialogAwareDataContext 接口,ViewModel 可以感知自身被关联到哪个 Dialog 实例,从而在 ViewModel 层主动关闭对话框、获取结果等。
功能详解
宿主类型(DialogHostType)
通过 DialogHostType 属性选择宿主:
- Overlay(默认):对话框渲染在当前窗口的 Overlay 层上,通过
OverlayDialogHost实现。支持拖拽移动、调整大小、遮罩层。 - Window:对话框在独立的
DialogHost(继承自Window)中渲染。支持系统窗口标题栏、最小化、最大化、原生拖拽。
模态与非模态
- 模态(IsModal = true,默认):打开后阻止用户与底层界面交互。
OpenAsync()返回的 Task 在对话框关闭后才完成,可以直接await获取Result。 - 非模态(IsModal = false):打开后不阻止用户操作底层界面。
OpenAsync()在对话框显示后立即完成。
标准按钮与自定义按钮
- 标准按钮:通过
StandardButtons属性配置,例如StandardButtons="Ok, Cancel"。 - 默认按钮:通过
DefaultStandardButton指定哪个标准按钮为默认确认按钮(视觉上高亮显示)。 - 自定义按钮:通过
CustomButtons集合添加DialogButton实例,可自定义文本、角色和样式。 - 按钮属性配置回调:
ButtonsConfigure回调可在按钮创建后统一修改属性(如禁用所有按钮)。
加载状态
- IsLoading:整体加载状态,对话框内容区域显示加载遮罩。
- IsConfirmLoading:确认按钮加载状态。当
IsConfirmLoading = true时,对话框不会响应关闭请求,防止用户在异步操作完成前关闭对话框。
拖拽与调整大小
- IsDragMovable:启用标题栏拖拽移动。
- IsResizable:启用对话框边缘拖拽调整大小。
- IsMaximizable:启用最大化按钮。
关闭流程
Dialog 提供三种关闭方式:
- Accept():设置
Result = DialogCode.Accepted,触发Accepted事件后关闭。 - Reject():设置
Result = DialogCode.Rejected,触发Rejected事件后关闭。 - Done(result?):设置自定义 Result 后关闭,触发
Finished事件。
关闭前会触发 Closing 事件(CancelEventArgs),可以通过设置 Cancel = true 阻止关闭。当 IsConfirmLoading = true 时,所有关闭请求均被忽略。
按钮角色到关闭行为的映射:
当底部按钮被点击时,Dialog 首先触发 ButtonClicked 事件。若未被 Handled,则根据按钮角色自动执行:
| 按钮角色 | 关闭行为 |
|---|---|
AcceptRole / YesRole / ApplyRole / ResetRole |
调用 Accept()(Result = Accepted) |
RejectRole / NoRole |
调用 Reject()(Result = Rejected) |
ActionRole / HelpRole / CustomRole / DestructiveRole |
不自动关闭,需在 ButtonClicked 事件中自行处理 |
静态 API
Dialog 提供了一组静态方法,用于在代码中快速创建和显示对话框,无需在 AXAML 中声明:
Dialog.ShowDialog(...)/Dialog.ShowDialogModal(...)— 同步打开Dialog.ShowDialogAsync(...)/Dialog.ShowDialogModalAsync(...)— 异步打开- 泛型版本
Dialog.ShowDialogModalAsync<TView, TViewModel>(...)— 用于自定义视图 + ViewModel
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
| 模态弹窗 | ✅ Modal |
✅ Dialog |
✅ 完全对齐(命名不同) |
| 标题 | ✅ title |
✅ Title 属性 |
✅ 完全对齐 |
| 内容 | ✅ children |
✅ Content 属性 |
✅ 完全对齐 |
| 页脚按钮 | ✅ footer / okText / cancelText |
✅ StandardButtons + CustomButtons |
✅ 增强:支持 18 种标准按钮 |
| 确认加载 | ✅ confirmLoading |
✅ IsConfirmLoading |
✅ 完全对齐 |
| 关闭按钮 | ✅ closable |
✅ IsClosable |
✅ 完全对齐 |
| 遮罩层 | ✅ mask |
✅ Overlay 模式自带遮罩 | ✅ 完全对齐 |
| 点击遮罩关闭 | ✅ maskClosable |
✅ IsLightDismissEnabled |
✅ 完全对齐 |
| 居中显示 | ✅ centered |
✅ HorizontalStartupLocation="Center" |
✅ 完全对齐(更灵活) |
| 宽度 | ✅ width |
✅ Width / MinWidth / MaxWidth |
✅ 完全对齐 |
| 加载中 | ✅ loading |
✅ IsLoading |
✅ 完全对齐 |
| 拖拽移动 | ❌ 不支持 | ✅ IsDragMovable |
⭐ AtomUI 增强 |
| 调整大小 | ❌ 不支持 | ✅ IsResizable |
⭐ AtomUI 增强 |
| 窗口宿主 | ❌ 不适用(Web) | ✅ DialogHostType.Window |
⭐ AtomUI 桌面端增强 |
| 最大化/最小化 | ❌ 不适用 | ✅ IsMaximizable / IsMinimizable |
⭐ AtomUI 桌面端增强 |
| 标准按钮系统 | ❌ 仅 Ok/Cancel | ✅ 18 种标准按钮 + 角色系统 | ⭐ AtomUI 增强 |
| MVVM 集成 | ❌ 不适用 | ✅ IDialogAwareDataContext |
⭐ AtomUI 增强 |
Modal.confirm() |
✅ 静态方法 | ✅ Dialog.ShowDialogModalAsync() |
✅ 完全对齐 |
继承关系
Avalonia.Controls.Primitives.TemplatedControl
└── AtomUI.Desktop.Controls.Dialog
├── implements IDialogHostProvider
├── implements IMotionAwareControl
└── implements IDialog
Dialog 直接继承自 Avalonia 的 TemplatedControl,未使用 ContentControl 或 Window。内容通过 Content 属性传递给宿主(OverlayDialogHost 或 DialogHost)进行渲染。Dialog 本身不参与可视化树的测量和排列(MeasureCore 返回空尺寸),其唯一职责是管理对话框的生命周期和状态。
各层级职责划分:
| 层级 | 提供的能力 |
|---|---|
TemplatedControl |
模板化控件基础设施 |
Dialog |
对话框生命周期管理(打开/关闭/结果)、双宿主架构、标准按钮系统、定位系统、加载状态、拖拽/调整大小、MVVM 集成 |
实现的接口:
| 接口 | 定义位置 | 作用 |
|---|---|---|
IDialog |
AtomUI.Desktop.Controls |
对话框核心协议:Title、Result、生命周期事件、Accept/Reject/Done |
IDialogHostProvider |
AtomUI.Desktop.Controls |
提供对宿主(DialogHost)的访问 |
IMotionAwareControl |
AtomUI.Controls.Shared |
支持 IsMotionEnabled 动画开关 |
源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 控件类 | src/AtomUI.Desktop.Controls/Dialog/Dialog.cs |
Dialog 核心实现 |
| 静态 API | src/AtomUI.Desktop.Controls/Dialog/Dialog.StaticAPI.cs |
静态方法 |
| 配置选项 | src/AtomUI.Desktop.Controls/Dialog/DialogOptions.cs |
静态 API 的配置参数 |
| 接口 | src/AtomUI.Desktop.Controls/Dialog/IDialog.cs |
IDialog 接口 |
| MVVM 接口 | src/AtomUI.Desktop.Controls/Dialog/IDialogAwareDataContext.cs |
ViewModel 感知接口 |
| 宿主类型 | src/AtomUI.Desktop.Controls/Dialog/DialogHostType.cs |
Overlay / Window 枚举 |
| 定位枚举 | src/AtomUI.Desktop.Controls/Dialog/DialogPlacement.cs |
水平/垂直锚点枚举 |
| 按钮系统 | src/AtomUI.Desktop.Controls/Dialog/ButtonBox/ |
DialogButton、DialogButtonRole、DialogStandardButton 等 |
| Token 定义 | src/AtomUI.Desktop.Controls/Dialog/DialogToken.cs |
组件级 Design Token |
| 主题 | src/AtomUI.Desktop.Controls/Dialog/Themes/ |
ControlTheme 及 code-behind |
| Gallery 示例 | controlgallery/AtomUIGallery/ShowCases/Views/Feedback/ModalShowCase.axaml |
使用范例 |
模板结构
Dialog 本身不直接渲染可视内容,而是将属性代理给宿主。根据 DialogHostType 的不同,使用不同的宿主模板:
Overlay 宿主模板结构
OverlayDialogHost
├── OverlayDialogMask ← 遮罩层(模态时显示)
└── OverlayDialogResizer (PART_Resizer) ← 调整大小控件
└── StackPanel
├── OverlayDialogHeader (PART_Header) ← 标题栏(标题、图标、关闭/最大化按钮)
├── ContentPresenter ← 内容区域
└── DialogButtonBox (PART_ButtonBox) ← 底部按钮区域
Window 宿主模板结构
DialogHost (extends Window)
├── 系统窗口标题栏 ← 原生标题栏(标题、最小化/最大化/关闭)
└── StackPanel
├── ContentPresenter ← 内容区域
└── DialogButtonBox (PART_ButtonBox) ← 底部按钮区域
模板部件常量
| 常量名 | 值 | 所属类 | 说明 |
|---|---|---|---|
OverlayDialogThemeConstants.ResizerPart |
"PART_Resizer" |
OverlayDialogHost | 调整大小控件 |
OverlayDialogThemeConstants.HeaderPart |
"PART_Header" |
OverlayDialogHost | 标题栏 |
OverlayDialogHeaderThemeConstants.CloseButtonPart |
"PART_CloseButton" |
OverlayDialogHeader | 关闭按钮 |
OverlayDialogHeaderThemeConstants.MaximizeButtonPart |
"PART_MaximizeButton" |
OverlayDialogHeader | 最大化按钮 |
DialogThemeConstants.ButtonBoxPart |
"PART_ButtonBox" |
Dialog/DialogHost | 按钮区域 |