Skip to content

Latest commit

 

History

History
429 lines (331 loc) · 17.6 KB

File metadata and controls

429 lines (331 loc) · 17.6 KB
titledescriptionms.topicms.datekeywordsms.localizationpriority
App notifications from UWP to WinUI 3 migration
This topic contains migration guidance in the app notifications feature area.
article
12/14/2021
Windows, App, SDK, migrate, migrating, migration, port, porting, push, notifications, toast, toast notifications, app notifications, uwp
medium

App notifications from UWP to WinUI 3 migration

The only difference when migrating app notification code from UWP to WinUI 3 is in handling the activation of notifications. Sending and managing app notifications remains exactly the same.

Note

The term "toast notification" is being replaced with "app notification". These terms both refer to the same feature of Windows, but over time we will phase out the use of "toast notification" in the documentation.

Note

Some information relates to pre-released product, which may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

Activation differences

CategoryUWPWinUI 3
Foreground activation entry pointOnActivated method inside App.xaml.cs is calledOnLaunched method inside App.xaml.cs is called.
Background activation entry pointHandled separately as a background taskSame as foreground activation. OnLaunched method inside App.xaml.cs is called. Use GetActivatedEventArgs to determine if the app should fully launch or just handle task and quit.
Window activationYour window is automatically brought to foreground when foreground activation occursYou must bring your window to the foreground if desired
CategoryUWPWinUI 3
Foreground activation entry pointOnActivated method inside App.xaml.cs is calledSubscribe to ToastNotificationManagerCompat.OnActivated event (or COM class for C++)
Background activation entry pointHandled separately as a background taskArrives through same ToastNotificationManagerCompat.OnActivated event (or COM class for C++)
Window activationYour window is automatically brought to foreground when foreground activation occursYou must bring your window to the foreground if desired

Migration for C# apps

Step 1: Install NuGet library

For a WinUI 3 app, you handle activation for notifications by using the AppNotificationManager class. This class is provided by the Microsoft.WindowsAppSDK Nuget package, which is included by default in the WinUI 3 Visual Studio project templates.

[!INCLUDE nuget package]

This package adds the ToastNotificationManagerCompat API.


Step 2: Update your manifest

In your Package.appxmanifest, add:

  1. Declaration for xmlns:com
  2. Declaration for xmlns:desktop
  3. In the IgnorableNamespaces attribute, com and desktop
  4. desktop:Extension for windows.toastNotificationActivation to declare your toast activator CLSID (using a new GUID of your choice).
  5. MSIX only: com:Extension for the COM activator using the GUID from step #4. Be sure to include the Arguments="----AppNotificationActivated:" so that you know your launch was from a notification
<!--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:ExtensionCategory="windows.toastNotificationActivation"> <desktop:ToastNotificationActivationToastActivatorCLSID="replaced-with-your-guid-C173E6ADF0C3" /> </desktop:Extension> <!--Register COM CLSID LocalServer32 registry key--> <com:ExtensionCategory="windows.comServer"> <com:ComServer> <com:ExeServerExecutable="YourProject.exe"Arguments="----AppNotificationActivated:"DisplayName="App notification activator"> <com:ClassId="replaced-with-your-guid-C173E6ADF0C3"DisplayName="App notification activator"/> </com:ExeServer> </com:ComServer> </com:Extension> </Extensions> </Application> </Applications> </Package>
<!--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 toast clicked--> <desktop:ExtensionCategory="windows.toastNotificationActivation"> <desktop:ToastNotificationActivationToastActivatorCLSID="replaced-with-your-guid-C173E6ADF0C3" /> </desktop:Extension> <!--Register COM CLSID LocalServer32 registry key--> <com:ExtensionCategory="windows.comServer"> <com:ComServer> <com:ExeServerExecutable="YourProject.exe"Arguments="-ToastActivated"DisplayName="Toast activator"> <com:ClassId="replaced-with-your-guid-C173E6ADF0C3"DisplayName="Toast activator"/> </com:ExeServer> </com:ComServer> </com:Extension> </Extensions> </Application> </Applications> </Package>

Step 3: Handle activation

In your app's startup code (typically App.xaml.cs), update your code using the following steps:

  1. In OnLaunched, get the default instance of the AppNotificationManager class.
  2. Register for the AppNotificationManager.NotificationInvoked event.
  3. Call Microsoft.Windows.AppNotifications.AppNotificationManager.Register to register your app to receive notification events. It is important that your call this method after registering the NotificationInvoked handler.
  4. Refactor your window launch/activation code into a dedicated LaunchAndBringToForegroundIfNeeded helper method, so you can call it from multiple places.
  5. Create a HandleNotification helper method, so that it can be called from multiple places.
  6. Call AppInstance.GetActivatedEventArgs and check the AppActivationArguments.Kind property of the returned object for the value ExtendedActivationKind.AppNotification.
  7. If the activation kind is not AppNotification call the LaunchAndBringToForegroundIfNeeded helper method.
  8. If the activation kind is AppNotification cast the AppActivationArguments.Data property to an AppNotificationActivatedEventArgs and pass it to the HandleNotification helper method.
  9. In your ApplicationManager.NotificationInvoked handler, call the HandleNotification helper method.
  10. Within your HandleNotification helper method, be sure to dispatch to the App or Window dispatcher before executing any UI-related code like showing a window or updating UI
  11. Migrate your old UWP OnActivated code that handled app notification activation to your new HandleNotification helper method.

Migrated App.xaml.cs

protectedoverridevoidOnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgsargs){m_window=newMainWindow();// 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.AppNotificationManagernotificationManager=AppNotificationManager.Default;notificationManager.NotificationInvoked+=NotificationManager_NotificationInvoked;notificationManager.Register();varactivatedArgs=Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();varactivationKind=activatedArgs.Kind;if(activationKind!=ExtendedActivationKind.AppNotification){LaunchAndBringToForegroundIfNeeded();}else{HandleNotification((AppNotificationActivatedEventArgs)activatedArgs.Data);}}privatevoidLaunchAndBringToForegroundIfNeeded(){if(m_window==null){m_window=newMainWindow();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);}}privatevoidNotificationManager_NotificationInvoked(AppNotificationManagersender,AppNotificationActivatedEventArgsargs){HandleNotification(args);}privatevoidHandleNotification(AppNotificationActivatedEventArgsargs){// Use the dispatcher from the window if present, otherwise the app dispatcher.vardispatcherQueue=m_window?.DispatcherQueue??DispatcherQueue.GetForCurrentThread();dispatcherQueue.TryEnqueue(asyncdelegate{if(args.Argument.Contains("action")){switch(args.Arguments["action"]){// Send a background message.case"sendMessage":stringmessage=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;}}else{Debug.Print("Notification args is null");}});}privatestaticclassWindowHelper{[DllImport("user32.dll")]privatestaticexternboolShowWindow(IntPtrhWnd,intnCmdShow);[DllImport("user32.dll")][return:MarshalAs(UnmanagedType.Bool)]privatestaticexternboolSetForegroundWindow(IntPtrhWnd);publicstaticvoidShowWindow(Windowwindow){// Bring the window to the foreground... first get the window handle...varhwnd=WinRT.Interop.WindowNative.GetWindowHandle(window);// Restore window if minimized... requires DLL import aboveShowWindow(hwnd,0x00000009);// And call SetForegroundWindow... requires DLL import aboveSetForegroundWindow(hwnd);}}

In your app's startup code (typically App.xaml.cs), , update your code using the following steps:

  1. Define and grab the app-level DispatcherQueue
  2. Register for the ToastNotificationManagerCompat.OnActivated event
  3. Refactor your window launch/activation code into a dedicated LaunchAndBringToForegroundIfNeeded method, so you can call it from multiple places
  4. Avoid launching your window if ToastNotificationManagerCompat.WasCurrentProcessToastActivated() returns true (when that method is true, your OnActivated event will be called next and you can choose to show a window within the event callback).
  5. Within your ToastNotificationManagerCompat.OnActivated, be sure to dispatch to the App or Window dispatcher before executing any UI-related code like showing a window or updating UI
  6. Migrate your old UWP OnActivated code that handled toast activation to your new ToastNotificationManagerCompat.OnActivated event handler, and migrate any background task toast activation code to your new ToastNotificationManagerCompat.OnActivated event handler.

Migrated App.xaml.cs

publicstaticDispatcherQueueDispatcherQueue{get;privateset;}protectedoverridevoidOnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgsargs){// Get the app-level dispatcher.DispatcherQueue=global::Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();// Register for toast activation. Requires Microsoft.Toolkit.Uwp.Notifications NuGet package version 7.0 or greater.ToastNotificationManagerCompat.OnActivated+=ToastNotificationManagerCompat_OnActivated;// If we weren't launched by an app, launch our window like normal.// Otherwise if launched by a toast, our OnActivated callback will be triggered.if(!ToastNotificationManagerCompat.WasCurrentProcessToastActivated()){LaunchAndBringToForegroundIfNeeded();}}privatevoidLaunchAndBringToForegroundIfNeeded(){if(m_window==null){m_window=newMainWindow();m_window.Activate();// Additionally we show using our helper, since if activated via a toast, it doesn't// activate the window correctly.WindowHelper.ShowWindow(m_window);}else{WindowHelper.ShowWindow(m_window);}}privatevoidToastNotificationManagerCompat_OnActivated(ToastNotificationActivatedEventArgsCompate){// Use the dispatcher from the window if present, otherwise the app dispatcher.vardispatcherQueue=m_window?.DispatcherQueue??App.DispatcherQueue;dispatcherQueue.TryEnqueue(delegate{varargs=ToastArguments.Parse(e.Argument);switch(args["action"]){// Send a background message.case"sendMessage":stringmessage=e.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;}});}privatestaticclassWindowHelper{[DllImport("user32.dll")]privatestaticexternboolShowWindow(IntPtrhWnd,intnCmdShow);[DllImport("user32.dll")][return:MarshalAs(UnmanagedType.Bool)]privatestaticexternboolSetForegroundWindow(IntPtrhWnd);publicstaticvoidShowWindow(Windowwindow){// Bring the window to the foreground... first get the window handle...varhwnd=WinRT.Interop.WindowNative.GetWindowHandle(window);// Restore window if minimized... requires DLL import aboveShowWindow(hwnd,0x00000009);// And call SetForegroundWindow... requires DLL import aboveSetForegroundWindow(hwnd);}}

Building app notification content

With Windows App SDK, you can still create app notification content using raw xml, but you can also create app notification content using the new AppNotificationsBuilder API which replaces the ToastContentBuilder class provided by the Windows Community Toolkit. Send the app notification by calling AppNotificationManager.Show. Mixing Windows Community Toolkit and App SDK APIs is not recommended.

usingMicrosoft.Windows.AppNotifications;usingMicrosoft.Windows.AppNotifications.Builder; ...var builder =newAppNotificationBuilder().AddText("Send a message.").AddTextBox("textBox").AddButton(newAppNotificationButton("Send").AddArgument("action","sendMessage"));varnotificationManager=AppNotificationManager.Default;notificationManager.Show(builder.BuildNotification());

You can continue to use the ToastContentBuilder class provided by the Windows Community Toolkit both to create app notification content and to send notifications in a WinUI 3 app.

usingMicrosoft.Toolkit.Uwp.Notifications; ... new ToastContentBuilder().AddText("Send a message.").AddInputTextBox("textBox").AddButton(newToastButton().SetContent("Send").AddArgument("action","sendMessage")).Show();

Related topics

close