Upravit

Sdílet prostřednictvím


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

Category UWP WinUI 3
Foreground activation entry point OnActivated method inside App.xaml.cs is called OnLaunched method inside App.xaml.cs is called.
Background activation entry point Handled separately as a background task Same 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 activation Your window is automatically brought to foreground when foreground activation occurs You 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.

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: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>

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


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);
    }
}

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.

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());