Guida introduttiva: Notifiche app in Windows App SDK
In questo argomento di avvio rapido si creerà un'applicazione Windows desktop che invia e riceve notifiche di app locali, note anche come notifiche di tipo avviso popup, usando Windows App SDK.
Importante
Le notifiche per un'app con privilegi elevati (amministratore) non sono attualmente supportate.
Prerequisiti
- Introduzione a WinUI
- Creare un nuovo progetto che usa Windows App SDK OPPURE usare Windows App SDK in un progetto esistente
Esempio di app
Questa guida introduttiva illustra il codice delle app di esempio di notifiche disponibili in GitHub.
Informazioni di riferimento sulle API
Per la documentazione di riferimento sulle API per le notifiche push, vedere Spazio dei nomi Microsoft.Windows.AppNotifications.
Passaggio 1: Aggiungere le dichiarazioni dello spazio dei nomi
Aggiungere lo spazio dei nomi per notifiche app Windows App SDK Microsoft.Windows.AppNotifications
.
using Microsoft.Windows.AppNotifications;
Passaggio 2: Aggiornare il manifesto dell'app
Se l'app non è in pacchetto ( ovvero manca l'identità del pacchetto in fase di esecuzione), passare al passaggio 3: Registrarsi per gestire una notifica dell'app.
Se l'app viene inserita in un pacchetto (incluso il pacchetto con la posizione esterna):
- Aprire Package.appxmanifest.
- Aggiungere gli spazi dei nomi
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
andxmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
a<Package>
- Aggiungere
<desktop:Extension>
perwindows.toastNotificationActivation
in modo da dichiarare il CLSID dell'attivatore COM. È possibile ottenere un CLSID passando a Crea GUID in Strumenti in Visual Studio. - Aggiungere
<com:Extension>
per l'attivatore COM usando lo stesso CLSID.- Specificare il file .exe nell'attributo
Executable
. Il file .exe deve essere lo stesso processo che chiamaRegister()
durante la registrazione dell'app per le notifiche, come descritto più dettagliatamente nel Passaggio 3. Nell'esempio seguente, usiamoExecutable="SampleApp\SampleApp.exe"
. - Specificare
Arguments="----AppNotificationActivated:"
per assicurarsi che Windows App SDK possa elaborare il payload della notifica come tipo AppNotification. - Specificare una
DisplayName
.
- Specificare il file .exe nell'attributo
Importante
Avviso: se si definisce un tipo di estendibilità dell'app Windows.Protocol nel manifesto appx con <uap:Protocol>
, facendo clic sulle notifiche verranno eseguiti nuovi processi della stessa app, anche se l'app è già in esecuzione.
<!--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>
Passaggio 3: Eseguire la registrazione per gestire una notifica dell'app
Registrare l'app per gestire le notifiche, quindi annullare la registrazione al termine dell'app.
Nel file App.xaml
, registrare per AppNotificationManager::Default().NotificationInvoked, poi richiamare AppNotificationManager::Default().Register. L'ordine di queste chiamate è importante.
Importante
È necessario richiedere PushNotificationManager::Default().Register prima di richiedere AppInstance.GetCurrent.GetActivatedEventArgs.
Quando l'app termina, richiedere AppNotificationManager::Default(). Annullare la registrazione() per liberare il server COM e consentire ai richiami successivi di avviare un nuovo processo.
// 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
}
}
}
Passaggio 4: Visualizzare una notifica dell'app
È NECESSARIO completare il passaggio 3: Eseguire la registrazione per gestire una notifica dell'app prima di procedere.
A questo punto si visualizzerà una semplice notifica dell'app con una appLogoOverride
immagine e un pulsante.
Creare la notifica dell'app usando la classe AppNotificationBuilder e quindi chiamare Show
. Per altre informazioni su come costruire la notifica dell'app tramite XML, vedere gli esempi in Contenuto dell'avviso popup e lo schema XML delle notifiche.
Nota
Se l'app è inclusa in un pacchetto con posizione esterna, l'icona dell'app nell'angolo superiore sinistro della notifica viene originata da package.manifest
. Se l'app non è inclusa nel pacchetto, l'icona viene originata esaminando prima il collegamento, quindi esaminando il file di risorse nel processo dell'app. Se tutti i tentativi hanno esito negativo, viene usata l'icona dell'app predefinita di Windows. Sono supportati i tipi di file .jpg
, .png
, .bmp
, e .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.
Passaggio 5: Elaborare un utente selezionando una notifica
Gli utenti possono selezionare il corpo o il pulsante della notifica. L'app deve elaborare la chiamata in risposta a un utente che interagisce con la notifica.
Esistono due modi comuni per elaborare questa procedura:
- Si sceglie di avviare l'app in un contesto dell'interfaccia utente specifico OPPURE
- Si sceglie di fare in modo che l'app valuti un comportamento specifico dell'azione (ad esempio un pulsante premuto nel corpo della notifica) senza eseguire il rendering dell'interfaccia utente. Ciò è noto anche come azione in background.
L'esempio di codice seguente, che non proviene dall'app di esempio, illustra entrambi i modi di elaborazione di un'azione generata dall'utente. Aggiungere un launch
valore (corrisponde all'utente facendo clic sul corpo della notifica), un input
elemento (casella di testo risposta rapida) e un pulsante con un arguments
valore (corrisponde all'utente che fa clic sul pulsante) al payload XML della notifica. In questo ProcessLaunchActivationArgs
caso per ciascun argomento.
Importante
L'impostazione activationType="background"
nel payload XML di notifica viene ignorata per le app desktop. È invece necessario elaborare gli argomenti di attivazione e decidere se visualizzare una finestra o meno, come indicato in questo passaggio.
// 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&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&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);
}
}
Attenersi alle linee guida seguenti:
- Se l'utente seleziona una notifica e l'app non è in esecuzione, è previsto che l'app venga avviata e che l'utente possa visualizzare la finestra in primo piano nel contesto della notifica.
- Se l'utente seleziona una notifica e l'app viene ridotta a icona, è previsto che l'app venga portata in primo piano e che venga eseguito il rendering di una nuova finestra nel contesto della notifica.
- Se un'azione in background di notifica viene richiamata dall'utente (ad esempio, l'utente risponde a una notifica digitando nella casella di testo della notifica e selezionando la risposta), l'app elabora il payload senza eseguire il rendering di una finestra in primo piano.
Per un esempio più dettagliato, vedere il codice dell'app di esempio disponibile in GitHub .
Passaggio 6: Rimuovere le notifiche
Rimuovere le notifiche quando non sono più rilevanti per l'utente.
In questo esempio, l'utente ha visto tutti i messaggi di una chat di gruppo nell'app, in modo da cancellare tutte le notifiche dalla chat di gruppo. Quindi, l'utente disattiva un amico, in modo da cancellare tutte le notifiche dall'amico. In primo luogo, le proprietà Gruppo e Tag sono state aggiunte alle notifiche prima di visualizzarle per identificarle ora.
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);
}
Funzionalità aggiuntive
Invio di una notifica dell'app di origine cloud
Per inviare una notifica dell'app dal cloud, seguire Inviare una notifica dell'app di origine cloud all'indirizzo Avvio rapido: Notifiche push Windows App SDK.
Impostazione di un'ora di scadenza
Impostare un'ora di scadenza per la notifica dell'app usando la Expiration
proprietà se il messaggio nella notifica è rilevante solo per un determinato periodo di tempo. Ad esempio, se si invia un promemoria dell'evento del calendario, impostare l'ora di scadenza sulla fine dell'evento del calendario.
Nota
Il tempo di scadenza predefinito e massimo è di 3 giorni.
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)
}
}
Verificare che le notifiche scadano al riavvio
Impostare la ExpiresOnReboot
proprietà su Vero se si desidera che le notifiche vengano eliminate al riavvio.
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)
}
}
Inviare e aggiornare una notifica dell'indicatore di stato
È possibile visualizzare gli aggiornamenti correlati alla barra di stato in una notifica:
Usare il AppNotificationProgressData
costrutto per aggiornare la notifica dell'indicatore di stato.
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.
}
}