WpfNotifications 1.2.0
dotnet add package WpfNotifications --version 1.2.0
NuGet\Install-Package WpfNotifications -Version 1.2.0
<PackageReference Include="WpfNotifications" Version="1.2.0" />
<PackageVersion Include="WpfNotifications" Version="1.2.0" />
<PackageReference Include="WpfNotifications" />
paket add WpfNotifications --version 1.2.0
#r "nuget: WpfNotifications, 1.2.0"
#:package WpfNotifications@1.2.0
#addin nuget:?package=WpfNotifications&version=1.2.0
#tool nuget:?package=WpfNotifications&version=1.2.0
WpfNotifications
轻量级、可主题化的 WPF 应用内通知和多显示器桌面 Overlay 通知组件。
安装
Install-Package WpfNotifications
默认主题会通过 WPF 的主题机制自动加载,不再要求修改 App.xaml。旧项目中手动合并的 Styles/Generic.xaml 可以继续保留。
快速开始
在窗口中声明一个具有唯一标识的通知区域:
<ntf:NotificationArea xmlns:ntf="https://github.com/notifications/xaml/controls"
Identifier="main_window"
MaxItems="3"
NotificationMargin="10,10,10,0" />
向该区域发送通知:
using var manager = new NotificationManager();
manager.Show("普通消息", "main_window");
manager.Show(
"保存成功",
"文件已经写入磁盘",
NotificationType.Success,
"main_window");
省略区域标识符时,会使用不抢焦点的紧凑桌面 Overlay:
manager.Show("后台任务已经完成");
Show 和 Clear 可从后台线程调用,UI 操作会自动转发到管理器所属的 Dispatcher。旧 API 保持兼容。
后台旧 API 无法直接返回异常;可以订阅 manager.Error。需要可靠地等待结果或捕获异常时,优先使用下面的异步 API。
可观察的异步 API
新 API 使用明确的目标,并返回可控制单条通知的句柄。找不到区域或区域标识重复时会直接报告错误,不再静默丢弃消息。
var handle = await manager.ShowAsync(new NotificationRequest("正在下载…")
{
Target = NotificationTarget.Area("main_window"),
ExpirationTime = TimeSpan.MaxValue,
ShowCloseButton = true,
ShowCountdownBar = false,
Tag = "download",
});
await handle.UpdateAsync("下载完成");
var reason = await handle.Completion;
也可以主动关闭单条通知:
await handle.CloseAsync();
高级 API 默认不会因为点击内容而关闭;如需整条点击关闭,显式设置 CloseOnClick = true。
ShowCloseButton 和 ShowCountdownBar 可以控制单条通知的关闭按钮与倒计时条;保持为 null 时使用管理器默认值。隐藏倒计时条不会停止自动关闭。
直接使用 NotificationArea 时,可以通过显示选项获得相同行为:
area.Show(
"保存成功",
TimeSpan.FromSeconds(5),
new NotificationDisplayOptions
{
ShowCloseButton = true,
ShowCountdownBar = false,
PauseOnHover = true,
});
Completion 会返回 Programmatic、User、Expired、Cleared、Overflow、Replaced 或 ManagerDisposed,方便业务层准确处理生命周期。
常见调用可以使用更短的扩展方法:
using Notifications.Extensions;
var areaHandle = await manager.ShowAsync("保存成功", "main_window");
var overlayHandle = await manager.ShowOverlayAsync("后台任务完成", NotificationMonitor.MousePointer);
await manager.ClearAsync("main_window");
去重和更新
带相同 Tag 的活动通知可以保留多条、忽略新请求、更新原通知或替换原通知:
await manager.ShowAsync(new NotificationRequest("下载 50%")
{
Target = NotificationTarget.Area("main_window"),
Tag = "download",
DuplicateBehavior = NotificationDuplicateBehavior.UpdateExisting,
ExpirationTime = TimeSpan.MaxValue,
});
多显示器 Overlay
Overlay 可显示在主屏、鼠标当前所在屏或指定宿主窗口所在屏:
await manager.ShowAsync(new NotificationRequest("操作完成")
{
Target = NotificationTarget.Overlay(NotificationMonitor.MousePointer),
});
await manager.ShowAsync(new NotificationRequest("与窗口关联的消息")
{
Target = NotificationTarget.Overlay(NotificationMonitor.Owner, this),
});
Overlay 只占通知内容区域,使用 WS_EX_NOACTIVATE,不会因为透明全屏窗口遮挡其他应用;最后一条通知关闭后窗口会自动释放。
实例级配置
每个管理器可以拥有独立设置,不需要修改全局静态状态:
var manager = new NotificationManager(new NotificationManagerOptions
{
DefaultExpirationTime = TimeSpan.FromSeconds(8),
PauseOnHover = true,
PauseOnKeyboardFocus = true,
ShowCloseButton = true,
ShowCountdownBar = true,
Overlay = new NotificationOverlayOptions
{
MaxItems = 5,
Position = NotificationPosition.TopRight,
Margin = new Thickness(12),
Topmost = true,
},
});
管理器会复制一份配置快照。使用完成后应调用 Dispose();推荐通过 using 声明,它会关闭该管理器创建的活动通知并释放 Overlay 窗口:
using var manager = new NotificationManager(options);
Dispose() 发起清理;如果需要确认关闭动画和 Overlay 回收完成,请在 UI Dispatcher 退出前调用 await manager.DisposeAsync()。该方法在所有目标框架上返回 Task,也允许后台线程调用。重复调用会等待同一轮清理。
NotificationArea.ClearOnUnload 默认是 false,临时卸载或重新挂载不会清空通知。对页面退出后需要清理的区域,可设置 ClearOnUnload="True";整个窗口或服务退出时仍应释放其拥有的管理器。
ScheduleCloseAsync 观察的是一次定时安排:取消令牌会停止倒计时并保留通知,重新安排会结束旧任务,永久通知立即返回已完成任务。需要等待通知真正关闭时,使用 handle.Completion。公开关闭事件抛异常时,CloseAsync 会返回失败任务,但内部移除和生命周期完成仍会执行。
内部职责和维护约束见 架构说明。
为兼容旧代码,NotificationConstants 仍然有效;即使在 Application 创建前设置,管理器初始化时也会同步已配置的主题资源。
易用性和无障碍
- 默认模板提供可本地化的关闭按钮;应用内通知获得焦点后可按
Esc关闭。 - 鼠标悬停或键盘焦点进入时默认暂停倒计时。
- 通知使用 UI Automation Live Region,便于屏幕阅读器播报。
- 系统关闭客户端动画时,会跳过关闭动画等待。
- 内置状态色已调整为与白色文字满足更清晰的对比度。
- Overlay 默认最多显示 5 条,避免永久通知无限撑满屏幕。
传入 TimeSpan.MaxValue 可创建永久通知:
manager.Show("需要手动处理", "main_window", expirationTime: TimeSpan.MaxValue);
自定义内容和主题
可以传入普通对象、任意 UIElement,也可以继承 Notification。自定义通知可以继续使用 Close()、CloseAsync()、ScheduleClose(...) 和 ScheduleCloseAsync(...)。需要响应倒计时开始时,使用语义明确的 ExpirationScheduled 路由事件;旧 NotificationClosing 事件继续保留兼容性。
应用资源可覆盖默认主题:
<SolidColorBrush x:Key="Notifications.StringNotificationBackground"
Color="#242424" />
<SolidColorBrush x:Key="Notifications.DefaultNotificationForeground"
Color="White" />
<system:Double x:Key="Notifications.DefaultNotificationFontSize"
xmlns:system="clr-namespace:System;assembly=mscorlib">16</system:Double>
<FontWeight x:Key="Notifications.DefaultNotificationFontWeight">SemiBold</FontWeight>
入场和退出动画(1.2.0)
动画由 Notification 的生命周期统一驱动,默认模板和自定义模板均可使用,不再依赖默认样式的 EventTrigger。入场为 340 ms 淡入、24 DIP 上移,退出为 260 ms 淡出、下移;只改变 Opacity 与 RenderTransform,不压缩布局尺寸。关闭发生在入场途中时,从当前画面继续退出,CloseAsync() 等到动画完成后才移除通知。
<notifications:Notification OpeningAnimationDuration="0:0:0.34"
ClosingAnimationDuration="0:0:0.26"
AnimationDistance="24" />
自定义 ControlTemplate 建议将最外层视觉元素命名为 PART_AnimationRoot;没有该部件时会动画整个控件。无需再写淡入淡出 Storyboard。已有自定义动画的应用应移除重复 Storyboard,或用 AnimationsEnabled="False" 关闭库的动效。禁用动画、系统关闭客户端动画或时长为零时立即到达最终状态。
独立透明窗口应先测量、定位,再显示卡片:设置 DeferEntranceAnimation="True",等宿主就位后调用 notification.PlayEntranceAnimation()。该方法只播放一次;切换位置或恢复窗口不会重复入场。普通通知无需设置它。宿主在退出过程中卸载时,关闭任务会完成,不会一直等待不可见的动画。
目标框架
- 主支持:.NET 10、.NET 8(Windows/WPF)
- 兼容目标:.NET 9、7、6,以及 .NET Framework 4.7、4.8
.NET 6、7 已结束官方支持;新项目建议使用 .NET 10 LTS。CI 会构建全部目标框架,在 .NET 10、.NET 8 和 .NET Framework 4.8 上运行同一套 WPF STA 测试,并从生成的 NuGet 包创建干净 WPF 项目完成安装冒烟测试。
涉及布局、动画或 Overlay 行为的版本,发布前还应执行真实 UI 环境验收清单。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0-windows7.0 is compatible. net7.0-windows was computed. net7.0-windows7.0 is compatible. net8.0-windows was computed. net8.0-windows7.0 is compatible. net9.0-windows was computed. net9.0-windows7.0 is compatible. net10.0-windows was computed. net10.0-windows7.0 is compatible. |
| .NET Framework | net47 is compatible. net471 was computed. net472 was computed. net48 is compatible. net481 was computed. |
-
.NETFramework 4.7
- No dependencies.
-
.NETFramework 4.8
- No dependencies.
-
net10.0-windows7.0
- No dependencies.
-
net6.0-windows7.0
- No dependencies.
-
net7.0-windows7.0
- No dependencies.
-
net8.0-windows7.0
- No dependencies.
-
net9.0-windows7.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Adds template-independent fade/slide animations, configurable entrance/exit duration and distance, deferred entrance for custom hosts, interrupted-animation continuity, and completion-driven closing without layout scaling. Includes async disposal and lifecycle cleanup improvements.