从 UWP 到 WinUI 3 迁移的应用通知
将应用通知代码从 UWP 迁移到 WinUI 3 时,唯一的区别在于处理通知的激活。 发送和管理应用通知保持不变。
注意
术语“toast 通知”替换为“应用通知”。 两个术语指的是 Windows 的相同功能,但随着时间的推移,我们将逐步取消在文档中使用“toast 通知”。
注意
一些信息与预发布产品相关,在商业发行之前可能发生实质性修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。
激活差异
类别 | UWP | WinUI 3 |
---|---|---|
前台激活入口点 | OnActivated 调用内部 App.xaml.cs 方法 |
OnLaunched 调用内部 App.xaml.cs 的方法。 |
后台激活入口点 | 作为后台任务单独处理 | 与前台激活相同。 OnLaunched 调用内部 App.xaml.cs 的方法。 使用 GetActivatedEventArgs 来确定应用是否应完全启动或仅处理任务并退出。 |
窗口激活 | 当前台激活发生时,窗口会自动转到前台 | 如果需要,必须将窗口带到前台 |
C# 应用的迁移
步骤 1:安装 NuGet 库
对于 WinUI 3 应用,可以使用 AppNotificationManager 类处理通知的激活。 此类由 Microsoft.WindowsAppSDK Nuget 包提供,默认情况下包含在 WinUI 3 Visual Studio 项目模板中。
步骤 2:更新清单
在 Package.appxmanifest 中,添加:
- xmlns:com 声明
- xmlns:desktop 声明
- 在 IgnorableNamespaces 属性中,com 和 桌面
- windows.toastNotificationActivation 的 desktop:Extension,用于声明 toast 激活器 CLSID(使用你选择的新 GUID)。
- 仅限 MSIX:使用步骤 4 中 GUID 的 COM 激活器的 com:Extension。 务必包括
Arguments="----AppNotificationActivated:"
,以便了解是从通知启动
<!--Add these namespaces-->
<Package
...
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
IgnorableNamespaces="... com desktop">
...
<Applications>
<Application>
...
<Extensions>
<!--Specify which CLSID to activate when app notification clicked-->
<desktop:Extension Category="windows.toastNotificationActivation">
<desktop:ToastNotificationActivation ToastActivatorCLSID="replaced-with-your-guid-C173E6ADF0C3" />
</desktop:Extension>
<!--Register COM CLSID LocalServer32 registry key-->
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:ExeServer Executable="YourProject.exe" Arguments="----AppNotificationActivated:" DisplayName="App notification activator">
<com:Class Id="replaced-with-your-guid-C173E6ADF0C3" DisplayName="App notification activator"/>
</com:ExeServer>
</com:ComServer>
</com:Extension>
</Extensions>
</Application>
</Applications>
</Package>
步骤 3:处理激活
在应用的启动代码 (通常App.xaml.cs)中,使用以下步骤更新代码:
- 在 OnLaunched 中,获取 AppNotificationManager 类的默认实例。
- 注册 AppNotificationManager.NotificationInvoked 事件。
- 调用 Microsoft.Windows.AppNotifications.AppNotificationManager.Register 以注册应用以接收通知事件。 注册 NotificationInvoked 处理程序后,必须调用此方法。
- 将窗口启动/激活代码重构为专用
LaunchAndBringToForegroundIfNeeded
的帮助程序方法,以便可以从多个位置调用它。 - 创建
HandleNotification
帮助程序方法,以便可以从多个位置调用它。 - 调用 AppInstance.GetActivatedEventArgs 并检查返回对象的 AppActivationArguments.Kind 属性,以获取 ExtendedActivationKind.AppNotification 值。
- 如果激活类型不是 AppNotification , 请调用 LaunchAndBringToForegroundIfNeeded 帮助程序方法。
- 如果激活类型为 AppNotification,请将 AppActivationArguments.Data 属性强制转换为
HandleNotification
AppNotificationActivatedEventArgs,并将其传递给帮助程序方法。 - 在 ApplicationManager.NotificationInvoked 处理程序中,调用
HandleNotification
帮助程序方法。 HandleNotification
在帮助程序方法中,在执行任何与 UI 相关的代码(例如显示窗口或更新 UI)之前,请务必调度到应用或窗口调度程序- 将处理应用通知激活的旧 UWP
OnActivated
代码迁移到新的HandleNotification
帮助程序方法。
迁移App.xaml.cs
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
m_window = new MainWindow();
// To ensure all Notification handling happens in this process instance, register for
// NotificationInvoked before calling Register(). Without this a new process will
// be launched to handle the notification.
AppNotificationManager notificationManager = AppNotificationManager.Default;
notificationManager.NotificationInvoked += NotificationManager_NotificationInvoked;
notificationManager.Register();
var activatedArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
var activationKind = activatedArgs.Kind;
if (activationKind != ExtendedActivationKind.AppNotification)
{
LaunchAndBringToForegroundIfNeeded();
} else
{
HandleNotification((AppNotificationActivatedEventArgs)activatedArgs.Data);
}
}
private void LaunchAndBringToForegroundIfNeeded()
{
if (m_window == null)
{
m_window = new MainWindow();
m_window.Activate();
// Additionally we show using our helper, since if activated via a app notification, it doesn't
// activate the window correctly
WindowHelper.ShowWindow(m_window);
}
else
{
WindowHelper.ShowWindow(m_window);
}
}
private void NotificationManager_NotificationInvoked(AppNotificationManager sender, AppNotificationActivatedEventArgs args)
{
HandleNotification(args);
}
private void HandleNotification(AppNotificationActivatedEventArgs args)
{
// Use the dispatcher from the window if present, otherwise the app dispatcher
var dispatcherQueue = m_window?.DispatcherQueue ?? DispatcherQueue.GetForCurrentThread();
dispatcherQueue.TryEnqueue(async delegate
{
switch (args.Arguments["action"])
{
// Send a background message
case "sendMessage":
string message = args.UserInput["textBox"].ToString();
// TODO: Send it
// If the UI app isn't open
if (m_window == null)
{
// Close since we're done
Process.GetCurrentProcess().Kill();
}
break;
// View a message
case "viewMessage":
// Launch/bring window to foreground
LaunchAndBringToForegroundIfNeeded();
// TODO: Open the message
break;
}
});
}
private static class WindowHelper
{
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ShowWindow(Window window)
{
// Bring the window to the foreground... first get the window handle...
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
// Restore window if minimized... requires DLL import above
ShowWindow(hwnd, 0x00000009);
// And call SetForegroundWindow... requires DLL import above
SetForegroundWindow(hwnd);
}
}
生成应用通知内容
使用 Windows 应用 SDK,你仍然可以使用原始 xml 创建应用通知内容,但也可以使用新的 AppNotificationsBuilder API 创建应用通知内容,该 API 取代了 Windows 社区工具包提供的 ToastContentBuilder 类。 通过调用 AppNotificationManager.Show 发送应用通知。 不建议混合使用 Windows 社区工具包和应用 SDK API。
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
...
var builder = new AppNotificationBuilder()
.AddText("Send a message.")
.AddTextBox("textBox")
.AddButton(new AppNotificationButton("Send")
.AddArgument("action", "sendMessage"));
var notificationManager = AppNotificationManager.Default;
notificationManager.Show(builder.BuildNotification());