Краткое руководство. Уведомления о приложениях в пакете SDK для приложений Windows

A screen capture showing an app notification above the task bar. The notification is a reminder for an event. The app name, event name, event time, and event location are shown. A selection input displays the currently selected value,

В этом кратком руководстве вы создадите классическое приложение Windows, которое отправляет и получает уведомления локального приложения, также известные как всплывающие уведомления, с помощью пакета SDK для приложений Windows.

Важно!

Уведомления для приложения с повышенными привилегиями (привилегии администратора) в настоящее время не поддерживаются.

Необходимые компоненты

Пример приложения

В этом кратком руководстве рассматривается код из примеров приложений уведомлений, найденных на сайте GitHub.

Справочник по API

Справочная документация по API для уведомлений приложений см. в пространстве имен Microsoft.Windows.AppNotifications.

Шаг 1. Добавление объявлений пространства имен

Добавьте пространство имен для уведомлений Microsoft.Windows.AppNotificationsприложений пакета SDK для Windows.

using Microsoft.Windows.AppNotifications;

Шаг 2. Обновление манифеста приложения

Если приложение распаковывается (т. е. отсутствует удостоверение пакета во время выполнения), перейдите к шагу 3. Зарегистрируйтесь для обработки уведомления приложения.

Если приложение упаковано (включая пакет с внешним расположением):

  1. Откройте package.appxmanifest.
  2. Добавление xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" и xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" пространство имен в <Package>
  3. Добавьте <desktop:Extension> для объявления CLSID активатора windows.toastNotificationActivation COM. ClSID можно получить, перейдя к созданию GUID в разделе "Инструменты " в Visual Studio.
  4. Добавьте <com:Extension> для активатора COM с помощью того же CLSID.
    1. Укажите exe-файл в атрибуте Executable . Exe-файл должен быть тем же процессом, который вызывается Register() при регистрации приложения для уведомлений, который описан далее на шаге 3. В приведенном ниже примере мы используем Executable="SampleApp\SampleApp.exe".
    2. Укажите Arguments="----AppNotificationActivated:" , чтобы пакет SDK для приложений Windows может обрабатывать полезные данные уведомления как тип AppNotification.
    3. Укажите DisplayName.

Важно!

Предупреждение. Если вы определяете тип расширяемости приложения Windows.Protocol в манифесте <uap:Protocol>appx, то при нажатии кнопки "Уведомления" будут запускаться новые процессы того же приложения, даже если приложение уже запущено.

<!--Packaged apps only-->
<!--package.appxmanifest-->

<Package
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
  xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
  ...
  <Applications>
    <Application>
      ...
      <Extensions>

        <!--Specify which CLSID to activate when notification is clicked-->   
        <desktop:Extension Category="windows.toastNotificationActivation">
          <desktop:ToastNotificationActivation ToastActivatorCLSID="replaced-with-your-guid-C173E6ADF0C3" />
        </desktop:Extension>

        <!--Register COM CLSID-->    
        <com:Extension Category="windows.comServer">
          <com:ComServer>
            <com:ExeServer Executable="SampleApp\SampleApp.exe" DisplayName="SampleApp" Arguments="----AppNotificationActivated:">
              <com:Class Id="replaced-with-your-guid-C173E6ADF0C3" />
            </com:ExeServer>
          </com:ComServer>
        </com:Extension>
    
      </Extensions>
    </Application>
  </Applications>
 </Package>

Шаг 3. Регистрация для обработки уведомления приложения

Зарегистрируйте приложение для обработки уведомлений, а затем отмените регистрацию при завершении работы приложения.

App.xaml В файле зарегистрируйтесь для AppNotificationManager::D efault(). NotificationInvoked, а затем вызовите AppNotificationManager::D efault(). Зарегистрируйтесь. Порядок этих вызовов имеет значение.

Важно!

Необходимо вызвать AppNotificationManager::D efault(). Зарегистрируйтесь перед вызовом AppInstance.GetCurrent.GetActivatedEventArgs.

При завершении приложения вызовите AppNotificationManager::D efault(). Отмените регистрацию(), чтобы освободить COM-сервер и разрешить последующие вызовы для запуска нового процесса.

// App.xaml.cs
namespace CsUnpackagedAppNotifications
{

    public partial class App : Application
    {
        private Window mainWindow;
        private NotificationManager notificationManager;
        
        public App()
        {
            this.InitializeComponent();
            notificationManager = new NotificationManager();
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit);
        }

        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            mainWindow = new MainWindow();

            notificationManager.Init();
            
            // Complete in Step 5
            
            mainWindow.Activate();
        }

        void OnProcessExit(object sender, EventArgs e)
        {
            notificationManager.Unregister();
        }
    }
}


// NotificationManager.cs
namespace CsUnpackagedAppNotifications
{
    internal class NotificationManager
    {
        private bool m_isRegistered;

        private Dictionary<int, Action<AppNotificationActivatedEventArgs>> c_map;

        public NotificationManager()
        {
            m_isRegistered = false;

            // When adding new a scenario, be sure to add its notification handler here.
            c_map = new Dictionary<int, Action<AppNotificationActivatedEventArgs>>();
            c_map.Add(ToastWithAvatar.ScenarioId, ToastWithAvatar.NotificationReceived);
            c_map.Add(ToastWithTextBox.ScenarioId, ToastWithTextBox.NotificationReceived);
        }

        ~NotificationManager()
        {
            Unregister();
        }

        public void Init()
        {
            // 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 += OnNotificationInvoked;

            notificationManager.Register();
            m_isRegistered = true;
        }

        public void Unregister()
        {
            if (m_isRegistered)
            {
                AppNotificationManager.Default.Unregister();
                m_isRegistered = false;
            }
        }

        public void ProcessLaunchActivationArgs(AppNotificationActivatedEventArgs notificationActivatedEventArgs)
        {
            // Complete in Step 5
        }

    }
}       

Шаг 4. Отображение уведомления о приложении

App notification with button

Перед продолжением необходимо выполнить шаг 3. Зарегистрируйтесь для обработки уведомления приложения.

Теперь вы увидите простое уведомление приложения с изображением appLogoOverride и кнопкой.

Создайте уведомление приложения с помощью класса AppNotificationBuilder , а затем вызовите Show. Дополнительные сведения о создании уведомления приложения с помощью XML см. в примерах содержимого Toast и XML-схеме уведомлений.

Примечание.

Если приложение упаковано (в том числе упаковано с внешним расположением), значок вашего приложения в левом верхнем углу уведомления будет получен из package.manifestокна. Если приложение распаковывается, то значок создается при первом просмотре ярлыка, а затем просматривает файл ресурса в процессе приложения. Если все попытки завершаются сбоем, используется значок приложения Windows по умолчанию. Поддерживаемые типы файлов значков: .jpg, .bmp.pngи .ico.

// ToastWithAvatar.cs
class ToastWithAvatar
{
    public const int ScenarioId = 1;
    public const string ScenarioName = "Local Toast with Avatar Image";

    public static bool SendToast()
    {
        var appNotification = new AppNotificationBuilder()
            .AddArgument("action", "ToastClick")
            .AddArgument(Common.scenarioTag, ScenarioId.ToString())
            .SetAppLogoOverride(new System.Uri("file://" + App.GetFullPathToAsset("Square150x150Logo.png")), AppNotificationImageCrop.Circle)
            .AddText(ScenarioName)
            .AddText("This is an example message using XML")
            .AddButton(new AppNotificationButton("Open App")
                .AddArgument("action", "OpenApp")
                .AddArgument(Common.scenarioTag, ScenarioId.ToString()))
            .BuildNotification();

        AppNotificationManager.Default.Show(appNotification);

        return appNotification.Id != 0; // return true (indicating success) if the toast was sent (if it has an Id)
    }

    public static void NotificationReceived(AppNotificationActivatedEventArgs notificationActivatedEventArgs)
    {
        // Complete in Step 5   
    }
}

// Call SendToast() to send a notification. 

Шаг 5. Обработка уведомления пользователем

Пользователи могут выбрать текст или кнопку уведомления. Приложение должно обработать вызов в ответ на взаимодействие пользователя с уведомлением.

Существует 2 распространенных способа обработки этого:

  1. Вы решили запустить приложение в определенном контексте пользовательского интерфейса ИЛИ
  2. Вы решили оценить поведение приложения (например, нажатие кнопки в тексте уведомления) без отрисовки пользовательского интерфейса. Также называется фоновым действием.

Приведенный ниже пример кода, не полученный из примера приложения, иллюстрирует оба способа обработки действия, созданного пользователем. launch Добавьте значение (соответствует пользователю, щелкнув текст уведомления), input элемент (текстовое поле быстрого ответа) и кнопку со arguments значением (соответствует нажатию кнопки) для полезных данных XML уведомления. В вашем ProcessLaunchActivationArgsслучае по каждому аргументу.

Важно!

Параметр activationType="background" в полезных данных XML уведомления игнорируется для классических приложений. Вместо этого необходимо обработать аргументы активации и решить, следует ли отображать окно или нет, как указано на этом шаге.

App notification with reply

// Example of how to process a user either selecting the notification body or inputting a quick reply in the text box. 

// Notification XML payload
//<toast launch="action=openThread&amp;threadId=92187">
//  <visual>
//      <binding template="ToastGeneric">
//          <image placement="appLogoOverride" hint-crop="circle" src="C:\<fullpath>\Logo.png"/>
//          <text>Local Toast with Avatar and Text box</text>
//          <text>This is an example message using</text>
//      </binding>
//  </visual>
//  <actions>
//      <input id="replyBox" type="text" placeHolderContent="Reply" />
//      <action
//          content="Send"
//          hint-inputId="replyBox"
//          arguments="action=reply&amp;threadId=92187" />
//  </actions>
//</toast>

void ProcessLaunchActivationArgs(const winrt::AppNotificationActivatedEventArgs& notificationActivatedEventArgs)
{
    // If the user clicks on the notification body, your app needs to launch the chat thread window
    if (std::wstring(notificationActivatedEventArgs.Argument().c_str()).find(L"openThread") != std::wstring::npos)
    {
        GenerateChatThreadWindow();
    }
    else // If the user responds to a message by clicking a button in the notification, your app needs to reply back to the other user with no window launched
    if (std::wstring(notificationActivatedEventArgs.Argument().c_str()).find(L"reply") != std::wstring::npos)
    {
        auto input = notificationActivatedEventArgs.UserInput();
        auto replyBoxText = input.Lookup(L"replyBox");

        // Process the reply text
        SendReplyToUser(replyBoxText);
    }
}

Следуйте приведенным ниже рекомендациям.

  1. Если уведомление выбрано пользователем и приложение не запущено, ожидается, что приложение запускается, а пользователь может увидеть окно переднего плана в контексте уведомления.
  2. Если пользователь выбирает уведомление и приложение свернуто, ожидается, что ваше приложение будет доставлено на передний план, а новое окно отображается в контексте уведомления.
  3. Если фоновое действие уведомления вызывается пользователем (например, пользователь отвечает на уведомление, введя текстовое поле уведомления и нажав ответ), приложение обрабатывает полезные данные без отображения окна переднего плана.

Пример кода приложения, найденный на сайте GitHub , см. в более подробном примере.

Шаг 6. Удаление уведомлений

Удалите уведомления, когда они больше не относятся к пользователю.

В этом примере пользователь видел все сообщения из группового чата в приложении, поэтому вы очищаете все уведомления из группового чата. Затем пользователь отключает друг, поэтому вы очищаете все уведомления от друга. Сначала вы добавили свойства группы и тега в уведомления перед отображением, чтобы определить их сейчас.


void SendNotification(winrt::hstring const& payload, winrt::hstring const& friendId, winrt::hstring const& groupChatId)
{
    winrt::AppNotification notification(payload);

    // Setting Group Id here allows clearing notifications from a specific chat group later
    notification.Group(groupChatId);

    // Setting Tag Id here allows clearing notifications from a specific friend later
    notification.Tag(friendId);

    winrt::AppNotificationManager::Default().Show(notification);
}

winrt::Windows::Foundation::IAsyncAction RemoveAllNotificationsFromGroupChat(const std::wstring groupChatId)
{
    winrt::AppNotificationManager manager = winrt::AppNotificationManager::Default();
    co_await manager.RemoveByGroupAsync(groupChatId);    
}

winrt::Windows::Foundation::IAsyncAction RemoveAllNotificationsFromFriend(const std::wstring friendId)
{
    winrt::AppNotificationManager manager = winrt::AppNotificationManager::Default();
    co_await manager.RemoveByTagAsync(friendId);    
}

Дополнительные функции

Отправка уведомления об облачном приложении

Чтобы отправить уведомление о приложении из облака, следуйте инструкциям по отправке уведомления об облачном приложении в кратком руководстве: push-уведомления в пакете SDK для приложений Windows.

Установка срока действия

Задайте срок действия уведомления приложения с помощью Expiration свойства, если сообщение в уведомлении относится только к определенному периоду времени. Например, если вы отправляете напоминание о событии календаря, задайте время окончания события календаря.

Примечание.

Значение по умолчанию и максимальное время окончания срока действия — 3 дня.

class ToastWithAvatar
{
    public static bool SendToast()
    {

        var appNotification = new AppNotificationBuilder()
            .SetAppLogoOverride(new System.Uri("ms-appx:///images/logo.png"), AppNotificationImageCrop.Circle)
            .AddText("Example expiring notification")
            .AddText("This is an example message")
            .BuildNotification();


        appNotification.Expiration = DateTime.Now.AddDays(1);
        AppNotificationManager.Default.Show(appNotification);

        return appNotification.Id != 0; // return true (indicating success) if the toast was sent (if it has an Id)
    }
}

Убедитесь, что срок действия уведомлений истекает при перезагрузке

ExpiresOnReboot Задайте для свойства значение True, если вы хотите, чтобы уведомления были удалены при перезагрузке.

class ToastWithAvatar
{
    public static bool SendToast()
    {

        var appNotification = new AppNotificationBuilder()
            .SetAppLogoOverride(new System.Uri("ms-appx:///images/logo.png"), AppNotificationImageCrop.Circle)
            .AddText("Example ExpiresOnReboot notification")
            .AddText("This is an example message")
            .BuildNotification();


            appNotification.ExpiresOnReboot = true;
            AppNotificationManager.Default.Show(appNotification);

            return appNotification.Id != 0; // return true (indicating success) if the toast was sent (if it has an Id)
    }
}

Отправка и обновление уведомления о индикаторе выполнения

В уведомлении можно отобразить связанные обновления на панели выполнения:

Notification with progress bar

Используйте конструкцию AppNotificationProgressData для обновления уведомления панели хода выполнения.

const winrt::hstring c_tag = L"weekly-playlist";
const winrt::hstring c_group = L"downloads";

// Send first Notification Progress Update
void SendUpdatableNotificationWithProgress()
{
    auto notification{ winrt::AppNotificationBuilder()
            .AddText(L"Downloading this week's new music...")
            .AddProgressBar(winrt::AppNotificationProgressBar()
                .BindTitle()
                .BindValue()
                .BindValueStringOverride()
                .BindStatus())
            .BuildNotification() }

    notification.Tag(c_tag);
    notification.Group(c_group);

    // Assign initial values for first notification progress UI
    winrt::AppNotificationProgressData data(1); // Sequence number
    data.Title(L"Weekly playlist"); // Binds to {progressTitle} in xml payload
    data.Value(0.6); // Binds to {progressValue} in xml payload
    data.ValueStringOverride(L"15/26 songs"); // Binds to {progressValueString} in xml payload
    data.Status(L"Downloading..."); // Binds to {progressStatus} in xml payload

    notification.Progress(data);
    winrt::AppNotificationManager::Default().Show(notification);
}

// Send subsequent progress updates
winrt::Windows::Foundation::IAsyncAction UpdateProgressAsync()
{
    // Assign new values
    winrt::AppNotificationProgressData data(2 /* Sequence number */ );
    data.Title(L"Weekly playlist"); // Binds to {progressTitle} in xml payload
    data.Value(0.7); // Binds to {progressValue} in xml payload
    data.ValueStringOverride(L"18/26 songs"); // Binds to {progressValueString} in xml payload
    data.Status(L"Downloading..."); // Binds to {progressStatus} in xml payload

    auto result = co_await winrt::AppNotificationManager::Default().UpdateAsync(data, c_tag, c_group);
    if (result == winrt::AppNotificationProgressResult::AppNotificationNotFound)
    {
        // Progress Update failed since the previous notification update was dismissed by the user! So account for this in your logic by stopping updates or starting a new Progress Update flow.
    }
}

Ресурсы