14 KiB
Notification 通知提醒
概述
通知提醒框(Notification)在页面角落弹出全局通知,用于展示需要用户关注但不需要强制中断操作的信息。不同于 Message 的顶部居中短提示,Notification 更适合展示较长的通知内容,包含标题和正文,并支持自定义图标和自动关闭倒计时。
AtomUI 的 Notification 控件完整复刻了 Ant Design 5.0 Notification 的设计规范,在 .NET / Avalonia 平台上提供与 Ant Design React 版一致的视觉体验和交互行为。
设计原理
Ant Design 的通知设计哲学
Ant Design 对通知提醒框的定位是:「在页面的角落弹出全局通知,用于较复杂的通知内容」。与 Message 的区别在于:
| 对比维度 | Message 消息 | Notification 通知 |
|---|---|---|
| 弹出位置 | 页面顶部居中 | 页面四角或上下居中 |
| 内容复杂度 | 简短的一行文字 | 标题 + 正文,可包含自定义图标 |
| 关闭方式 | 自动关闭 | 自动关闭 + 手动关闭按钮 |
| 适用场景 | 操作反馈(成功/失败/警告) | 系统级通知、异步任务结果、较长描述信息 |
四种通知类型(通过图标和颜色区分语义):
| 类型 | 设计意图 | 图标 | 颜色 |
|---|---|---|---|
| ℹ️ Information | 一般性信息通知 | InfoCircleFilled |
主色(ColorPrimary) |
| ✅ Success | 操作成功反馈 | CheckCircleFilled |
成功色(ColorSuccess) |
| ⚠️ Warning | 警告通知 | ExclamationCircleFilled |
警告色(ColorWarning) |
| ❌ Error | 错误/失败通知 | CloseCircleFilled |
错误色(ColorError) |
六种弹出位置:
| 位置 | 说明 |
|---|---|
TopLeft |
左上角 |
TopRight |
右上角(默认) |
TopCenter |
顶部居中 |
BottomLeft |
左下角 |
BottomRight |
右下角 |
BottomCenter |
底部居中 |
Avalonia 基础能力
AtomUI 的 Notification 系统不继承自 Avalonia 内置的通知组件,而是完全自研实现,直接基于 Avalonia 基础控件构建:
NotificationCard继承自ContentControl,作为单条通知的可视化载体。WindowNotificationManager继承自TemplatedControl,负责通知的生命周期管理、定位和容器布局。
核心设计思路:
WindowNotificationManager (容器,挂载到 AdornerLayer)
└── ReversibleStackPanel (PART_Items)
├── NotificationCard (单条通知)
├── NotificationCard (单条通知)
└── ...
WindowNotificationManager 通过 AdornerLayer 挂载到窗口的最顶层,确保通知悬浮于所有业务内容之上。内部使用 ReversibleStackPanel 管理通知卡片的排列方向。
AtomUI 的实现设计
| 设计能力 | 实现方式 | 设计动机 |
|---|---|---|
| 四种通知类型 | NotificationType 枚举 + 伪类驱动图标/颜色 |
对齐 Ant Design 的通知类型系统 |
| 六种弹出位置 | NotificationPosition 枚举 + 伪类驱动布局 |
支持页面四角及上下居中 |
| 自定义图标 | PathIcon? Icon 属性 |
允许替换默认类型图标 |
| 自动关闭 | Expiration + DispatcherTimer 周期检测 |
超时自动关闭通知 |
| 手动关闭 | IconButton 关闭按钮 |
右上角关闭按钮 |
| 进度条 | NotificationProgressBar + ShowProgress |
可视化自动关闭倒计时 |
| 悬停暂停 | IsPauseOnHover + 指针事件控制计时器 |
鼠标悬停时暂停自动关闭倒计时 |
| 出入动画 | 方向感知的自定义 Motion 动画 | 根据位置选择合适的滑入/滑出方向 |
| 最大通知数限制 | MaxItems + 超限自动关闭旧通知 |
防止通知堆积占满屏幕 |
| Design Token | NotificationToken + RegisterTokenResourceScope |
所有视觉值从全局/组件级 Token 派生 |
功能详解
通知类型(NotificationType)
通知类型通过 Notification 构造函数的 type 参数设置,在 NotificationCard 中通过 NotificationType 属性传递。每种类型自动分配对应的图标和颜色:
| 类型 | 默认图标 | 图标颜色 Token |
|---|---|---|
Information |
InfoCircleFilled |
ColorPrimary |
Success |
CheckCircleFilled |
ColorSuccess |
Warning |
ExclamationCircleFilled |
ColorWarning |
Error |
CloseCircleFilled |
ColorError |
如果提供了自定义 Icon,则使用自定义图标替换默认图标。
弹出位置(NotificationPosition)
弹出位置通过 WindowNotificationManager.Position 属性设置。不同位置影响三个方面:
- 容器布局:通过
ReversibleStackPanel的HorizontalAlignment和VerticalAlignment控制。 - 卡片边距:顶部位置使用
NotificationTopMargin,底部位置使用NotificationBottomMargin。 - 出入动画方向:
TopLeft/BottomLeft→ 从左侧滑入/滑出TopRight/BottomRight→ 从右侧滑入/滑出TopCenter→ 从上方滑入/滑出BottomCenter→ 从下方滑入/滑出
自动关闭与进度条
通知的自动关闭由 Expiration 属性控制:
- 默认超时时间为 5 秒(
TimeSpan.FromSeconds(5))。 - 设置
Expiration = TimeSpan.Zero可使通知永不自动关闭,仅能通过关闭按钮手动关闭。 - 当
ShowProgress = true且Expiration > TimeSpan.Zero时,通知底部会显示一个渐变进度条,随倒计时逐渐缩短。
悬停暂停(Pause on Hover)
当 WindowNotificationManager.IsPauseOnHover = true(默认开启)时,鼠标悬停在通知卡片上会暂停所有通知的自动关闭计时器,鼠标移出后恢复。这确保用户在阅读通知内容时不会被意外关闭。
出入动画
通知的出入动画通过 MotionActor 机制实现,使用自定义的 AbstractMotion 子类:
| 位置 | 进入动画 | 退出动画 |
|---|---|---|
TopLeft / BottomLeft |
NotificationMoveLeftInMotion |
NotificationMoveLeftOutMotion |
TopRight / BottomRight |
NotificationMoveRightInMotion |
NotificationMoveRightOutMotion |
TopCenter |
NotificationMoveUpInMotion |
NotificationMoveUpOutMotion |
BottomCenter |
NotificationMoveDownInMotion |
NotificationMoveDownOutMotion |
每个动画包含三阶段关键帧(开始 → 中间 → 结束),同时控制 Opacity(透明度)和 TranslateTransform(位移),实现平滑的滑入/滑出效果。动画时长由 SharedToken.MotionDurationMid 控制。
最大通知数限制
WindowNotificationManager.MaxItems 属性(默认值 5)限制同时可见的通知数量。当新通知到来使数量超限时,最早的通知会被自动关闭(触发退出动画后移除),确保通知区域不会无限膨胀。
与 Ant Design 规范的对齐程度
| 特性 | Ant Design (React) | AtomUI (Avalonia) | 对齐情况 |
|---|---|---|---|
四种类型 type |
✅ success / info / warning / error |
✅ NotificationType 枚举 |
✅ 完全对齐 |
弹出位置 placement |
✅ top / bottom / topLeft / topRight / bottomLeft / bottomRight |
✅ NotificationPosition 枚举 |
✅ 完全对齐 |
自动关闭 duration |
✅ 默认 4.5s,0 为不关闭 |
✅ 默认 5s,TimeSpan.Zero 为不关闭 |
⚠️ 默认时长略有不同 |
自定义图标 icon |
✅ ReactNode | ✅ PathIcon? 属性 |
✅ 对齐(类型不同,语义一致) |
| 关闭按钮 | ✅ 右上角关闭 | ✅ IconButton 关闭按钮 |
✅ 完全对齐 |
进度条 showProgress |
✅ 5.18.0 新增 | ✅ ShowProgress 属性 |
✅ 完全对齐 |
悬停暂停 pauseOnHover |
✅ 5.18.0 新增 | ✅ IsPauseOnHover 属性 |
✅ 完全对齐 |
自定义操作按钮 btn |
✅ ReactNode | ❌ 暂未支持 | ⚠️ 待支持 |
底部区域 description |
✅ 单独 description | ✅ 通过 Content 实现 |
✅ 语义对齐 |
点击回调 onClick |
✅ 函数 | ✅ OnClick Action |
✅ 完全对齐 |
关闭回调 onClose |
✅ 函数 | ✅ OnClose Action |
✅ 完全对齐 |
全局配置 maxCount |
✅ 最大显示数 | ✅ MaxItems 属性 |
✅ 完全对齐 |
堆叠模式 stack |
✅ 5.18.0 新增 | ❌ 暂未支持 | ⚠️ 待支持 |
编程式 API api.open() |
✅ hooks API | ⚠️ 通过 INotificationManager.Show() 方法调用 |
✅ 等效实现 |
继承关系
WindowNotificationManager
Avalonia.Controls.Primitives.TemplatedControl
└── AtomUI.Desktop.Controls.WindowNotificationManager
├── implements INotificationManager
├── implements IMotionAwareControl
└── implements IDisposable
NotificationCard
Avalonia.Controls.Primitives.TemplatedControl
└── Avalonia.Controls.ContentControl
└── AtomUI.Desktop.Controls.NotificationCard
└── implements IMotionAwareControl
Notification(数据模型)
AtomUI.Desktop.Controls.Notification
├── implements INotification
└── implements INotifyPropertyChanged
各层级职责划分:
| 类 | 职责 |
|---|---|
INotification |
通知数据契约:标题、内容、类型、图标、过期时间、回调 |
Notification |
INotification 的默认实现,支持属性变更通知 |
INotificationManager |
通知管理器契约:Show() 方法 |
WindowNotificationManager |
通知管理器实现:挂载到窗口 AdornerLayer、管理通知生命周期、定时关闭、位置控制 |
NotificationCard |
单条通知的可视化控件:图标、标题、内容、关闭按钮、进度条、出入动画 |
NotificationProgressBar |
通知内的进度条控件(internal),绘制倒计时进度指示 |
源码位置
| 层级 | 文件路径 | 说明 |
|---|---|---|
| 接口 | src/AtomUI.Desktop.Controls/Notifications/INotification.cs |
通知数据契约接口 |
| 接口 | src/AtomUI.Desktop.Controls/Notifications/INotificationManager.cs |
通知管理器契约接口 |
| 数据模型 | src/AtomUI.Desktop.Controls/Notifications/Notification.cs |
INotification 默认实现 |
| 控件类 | src/AtomUI.Desktop.Controls/Notifications/NotificationCard.cs |
通知卡片控件 |
| 控件类 | src/AtomUI.Desktop.Controls/Notifications/WindowNotificationManager.cs |
通知管理器控件 |
| 进度条 | src/AtomUI.Desktop.Controls/Notifications/NotificationProgressBar.cs |
内部进度条控件 |
| 枚举 | src/AtomUI.Desktop.Controls/Notifications/NotificationType.cs |
通知类型枚举 |
| 枚举 | src/AtomUI.Desktop.Controls/Notifications/NotificationPosition.cs |
通知位置枚举 |
| 伪类常量 | src/AtomUI.Desktop.Controls/Notifications/NotificationPseudoClass.cs |
位置伪类常量 |
| Token 定义 | src/AtomUI.Desktop.Controls/Notifications/NotificationToken.cs |
组件级 Design Token |
| 动画 | src/AtomUI.Desktop.Controls/Notifications/NotificationMotions.cs |
8 种方向动画定义 |
| 工具类 | src/AtomUI.Desktop.Controls/Notifications/Utils/NotificationProgressBarVisibleConverter.cs |
进度条可见性转换器 |
| 主题模板 | src/AtomUI.Desktop.Controls/Notifications/Themes/NotificationCardTheme.axaml |
通知卡片主题 |
| 主题模板 | src/AtomUI.Desktop.Controls/Notifications/Themes/WindowNotificationManagerTheme.axaml |
管理器主题 |
| 主题模板 | src/AtomUI.Desktop.Controls/Notifications/Themes/NotificationProgressBarTheme.axaml |
进度条主题 |
| 主题注册 | src/AtomUI.Desktop.Controls/Notifications/Themes/NotificationsThemes.axaml |
ResourceDictionary 汇总 |
| Gallery 示例 | controlgallery/AtomUIGallery/ShowCases/Views/Feedback/NotificationShowCase.axaml |
使用范例 |
| Gallery 代码 | controlgallery/AtomUIGallery/ShowCases/Views/Feedback/NotificationShowCase.axaml.cs |
范例 code-behind |
模板结构
NotificationCard 模板
LayoutAwareMotionActor (PART_MotionActor) ← 动画容器,出入动画作用目标
└── Border#Frame ← 主框架(背景、圆角、阴影)
└── Grid [Auto,*] × [Auto,*,Auto] ← 内容网格布局
├── IconPresenter#IconPresenter ← 类型图标(Row=0, Col=0)
├── DockPanel#HeaderContainer ← 标题栏容器(Row=0, Col=1)
│ ├── IconButton#PART_CloseButton ← 关闭按钮(DockPanel.Dock=Right)
│ └── SelectableTextBlock#HeaderTitle ← 标题文本
├── ContentPresenter#Content ← 正文内容(Row=1, Col=1)
└── NotificationProgressBar#ProgressBar ← 进度条(Row=1, Col=0, ColSpan=2)
布局设计要点:
- 动画层独立:
LayoutAwareMotionActor包裹整个通知卡片,确保出入动画不受布局约束影响。 - 图标与标题对齐:Grid 的第一列(Auto)放置图标,第二列(
*)放置标题和内容,图标与标题行顶部对齐。 - 关闭按钮右对齐:通过
DockPanel将关闭按钮停靠在标题栏右侧。 - 进度条底部:进度条位于内容区底部,跨越两列。
WindowNotificationManager 模板
ReversibleStackPanel (PART_Items) ← 通知容器,支持正序/逆序排列
├── NotificationCard ← 单条通知
├── NotificationCard ← 单条通知
└── ...
- 顶部位置(Top*)使用
ReverseOrder=True,使新通知出现在底部(从上往下堆叠)。 - 底部位置(Bottom*)使用正序排列,新通知出现在顶部。