Icone nella system tray e notifiche toast nelle app Windows — le insidie di NotifyIcon e come scegliere l'AppNotification giusta

· · System Tray, NotifyIcon, Notifiche Toast, AppNotification, Windows App SDK, WinForms, WPF, C#, .NET, App residente, Sviluppo Windows, Consulenza tecnica

“Voglio avvisare discretamente l’utente quando un’elaborazione termina.” “Voglio che il monitoraggio continui anche dopo la chiusura della finestra.” Sono due requisiti classici che spesso compaiono insieme nelle applicazioni aziendali: restare residenti nella system tray (area di notifica) e inviare notifiche toast. Ma non appena si passa all’implementazione reale, iniziano a emergere una serie di piccoli problemi: un’app che dovrebbe chiudersi in realtà non termina, l’icona scompare dopo il riavvio di Esplora risorse, oppure un toast che funzionava perfettamente in fase di sviluppo non compare mai presso il cliente.

Inoltre, le notifiche toast sono un’area in cui l’API ha continuato a cambiare generazione fin dall’epoca UWP. Non è raro cercare online e trovare codice di esempio che, secondo gli standard attuali, usa un’API ormai al più in sola manutenzione. Questo articolo organizza sia la progettazione della residenza in tray con NotifyIcon sia la scelta di un’API per le notifiche toast a partire dal 2026, insieme alle insidie pratiche che è probabile incontrare.

1. Prima di tutto, la conclusione

  • La base della residenza in tray è ancora System.Windows.Forms.NotifyIcon. Non compare finché non si imposta Icon e non si mette Visible = true (il valore predefinito è false). Il testo del tooltip Text può arrivare fino a 127 caratteri a partire da .NET 6 (63 caratteri prima di allora).12
  • WPF non ha alcun controllo equivalente a NotifyIcon. La tabella di confronto ufficiale indica esplicitamente “Nessun controllo equivalente”. Abilitare sia UseWPF sia UseWindowsForms nel csproj e usare il NotifyIcon di WinForms è la configurazione minima.34
  • “Mantenere l’app nella tray anche quando l’utente clicca sul pulsante di chiusura” si implementa impostando e.Cancel = true in FormClosing e chiamando Hide(). Fornire sempre un modo di uscita esplicito e separato (una voce “Esci” nel menu della tray), e controllare CloseReason per non bloccare mai un arresto del sistema operativo.5
  • L’API di toast raccomandata è chiara a partire dal 2026. Indipendentemente dal fatto che si stia costruendo un’app WinForms, WPF o non pacchettizzata, si raccomanda l’AppNotificationManager del Windows App SDK (NuGet: Microsoft.WindowsAppSDK). Windows.UI.Notifications è “solo manutenzione”, e il componente di notifiche del Community Toolkit è archiviato.67
  • L’AppNotification del Windows App SDK funziona anche da un semplice exe senza identità di pacchetto. AppNotificationManager.Default.Register() gestisce automaticamente la registrazione del server COM e il lavoro equivalente all’AUMID, quindi non serve più configurare un collegamento nel menu Start come richiedevano le generazioni precedenti. Detto ciò, le notifiche da processi elevati (amministratore) non sono supportate.8910
  • Un toast non è un modo garantito per raggiungere l’utente. Mentre l’assistente notifiche (non disturbare) è attivo, il banner non compare e la notifica va direttamente nel centro notifiche, e l’utente può anche disattivare del tutto le notifiche nelle impostazioni. La progettazione non deve presupporre che “il toast sia stato mostrato” come precondizione di un flusso di lavoro aziendale.1112
  • Un servizio Windows non può inviare toast (la Sessione 0 non ha interfaccia utente). L’approccio standard consiste nel collocare un agente residente in tray nella sessione dell’utente e coordinarsi tramite IPC.13

2. Le basi della residenza in tray con NotifyIcon

System.Windows.Forms.NotifyIcon è il componente che colloca un’icona nell’area di notifica all’estremità destra della barra delle applicazioni. Le app che continuano a funzionare in background lo usano come punto di ingresso per la visualizzazione dello stato e un menu operativo.1

private NotifyIcon _trayIcon = null!;

private void InitializeTrayIcon()
{
    var menu = new ContextMenuStrip();
    menu.Items.Add("Apri", null, (_, _) => ShowMainWindow());
    menu.Items.Add(new ToolStripSeparator());
    menu.Items.Add("Esci", null, (_, _) => ExitApplication());

    _trayIcon = new NotifyIcon
    {
        Icon = Properties.Resources.AppIcon, // L'icona è obbligatoria; senza di essa non viene mostrato nulla
        Text = "Strumento di monitoraggio ordini ── in esecuzione",     // Tooltip (massimo 127 caratteri su .NET 6+)
        ContextMenuStrip = menu,
        Visible = true                        // Il valore predefinito è false; viene mostrato solo impostandolo su true
    };
    _trayIcon.DoubleClick += (_, _) => ShowMainWindow();
}

Ci sono tre punti da tenere a mente.

  • L’icona compare solo quando sono impostati insieme Icon e Visible = true. Il valore predefinito di Visible è false.1
  • Text ha un limite di lunghezza. È al massimo di 127 caratteri a partire da .NET 6, e di 63 caratteri per .NET 5 e versioni precedenti nonché per .NET Framework; superarlo genera un’ArgumentException. Stipare troppe informazioni di stato nel tooltip è un ottimo modo per far sì che l’app lanci improvvisamente un’eccezione a un certo punto.214
  • Associare un menu al clic destro tramite ContextMenuStrip. L’icona nella tray è spesso l’unico punto di ingresso visibile all’utente, quindi fornire almeno una voce “Apri” e una “Esci”.15

Un’altra cosa: l’icona nella tray non è solo un punto di ingresso per le notifiche, ma anche un indicatore di stato sempre visibile. Poiché Icon e Text possono essere sostituiti mentre l’app è in esecuzione, riflettere lo stato corrente — un’icona normale quando tutto va bene, un’icona di avviso in caso di problemi, mostrare l’ultimo orario di elaborazione nel tooltip — significa che l’utente può dare un’occhiata all’icona e conoscere lo stato attuale anche se ha perso un toast. Il toast è il canale per “qualcosa è appena successo in questo momento”, mentre l’icona nella tray è il canale per “ecco lo stato attuale” — questa è la divisione dei compiti.

3. Progettare “resta nella tray quando chiuso” e un percorso di uscita corretto

Un comportamento classico delle app residenti è: cliccare sul pulsante di chiusura non termina il processo, lo lascia semplicemente nella tray. L’implementazione in sé è facile da scrivere annullando FormClosing, ma scriverla con noncuranza porta a due tipi di problemi: non c’è più alcun modo di uscire dall’app, e si blocca l’arresto di Windows.

private bool _exitRequested;

protected override void OnFormClosing(FormClosingEventArgs e)
{
    // Se l'utente ha semplicemente cliccato sulla X, nascondi invece di chiudere
    if (!_exitRequested && e.CloseReason == CloseReason.UserClosing)
    {
        e.Cancel = true;
        Hide();
        return;
    }
    // Chiusura normale tramite il menu "Esci", arresto del sistema operativo, ecc.
    base.OnFormClosing(e);
}

private void ExitApplication()
{
    _exitRequested = true;
    _trayIcon.Visible = false;
    _trayIcon.Dispose();
    Close();
}

FormClosing si attiva prima che il form si chiuda, e impostare e.Cancel = true annulla la chiusura. In questo momento, controllare sempre CloseReason. Il comportamento corretto è annullare solo per UserClosing (un’azione dell’utente), e non annullare mai per una chiusura innescata dal sistema operativo come WindowsShutDown.516

WPF segue la stessa logica: la documentazione ufficiale stessa descrive lo schema di impostare e.Cancel = true in Window.Closing e chiamare Hide().17

Va notato che, per sua natura, un’app residente è sempre a rischio del problema “se viene avviata due volte, lo stesso evento viene elaborato due volte”. Abbiamo trattato la prevenzione degli avvii multipli in “Impedire l’avvio multiplo di un’app Windows — mutex con nome e attivazione dell’istanza in esecuzione in caso di secondo avvio”. E “Perché usare .NET Generic Host e BackgroundService in un’app desktop” costituisce la base per costruire il contenuto vero e proprio di un’app residente — il ciclo di monitoraggio o l’elaborazione periodica.

4. Residenza in tray in un’app WPF

WPF non ha alcun controllo equivalente a NotifyIcon. Si tratta di una lacuna documentata della piattaforma, indicata esplicitamente come “No equivalent control” nella tabella di confronto ufficiale “controlli WPF equivalenti ai controlli WinForms”.3

Ci sono due opzioni realistiche.

Opzione Panoramica Indicazione
Usare il NotifyIcon di WinForms insieme a WPF Abilitare sia UseWPF sia UseWindowsForms nel csproj Nessuna dipendenza aggiuntiva. Sufficiente se serve solo un’icona e un menu
Terze parti (ad esempio H.NotifyIcon) Implementazioni di icone tray pensate per WPF/WinUI Da scegliere se si vuole scrivere in XAML o serve un popup più ricco. Tenere presente che non è un componente ufficiale Microsoft

La prima opzione consiste semplicemente nell’elencare due proprietà fianco a fianco in un file di progetto in stile SDK.4

<PropertyGroup>
  <TargetFramework>net8.0-windows</TargetFramework>
  <UseWPF>true</UseWPF>
  <UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>

Si può quindi semplicemente istanziare un System.Windows.Forms.NotifyIcon direttamente da un’Application WPF. Solo il menu finisce per essere di tipo WinForms (ContextMenuStrip), ma per qualcosa di semplice come un menu della tray, questo raramente costituisce un problema pratico.

5. Tre insidie delle icone nella tray

5.1. Le icone finiscono per default nelle “icone nascoste” — non è possibile forzarne la visualizzazione

La richiesta di mantenere l’icona della propria app sempre visibile sulla barra delle applicazioni arriva sempre, ma non è realizzabile a livello di programmazione. La documentazione ufficiale afferma chiaramente che le icone vengono aggiunte per default all’area di overflow (icone nascoste), e solo l’utente può decidere se promuovere un’icona alla visualizzazione permanente nell’area di notifica.18

Su Windows 11, l’utente attiva o disattiva questo comportamento per ogni singola app tramite “Impostazioni → Personalizzazione → Barra delle applicazioni → Altre icone nell’area di notifica”. Se la visibilità permanente è operativamente importante, la mossa corretta è documentarla nella guida di installazione o nella guida in linea (“attivare questa impostazione”), non cercare di forzarla nel codice.

5.2. L’icona scompare quando Esplora risorse si riavvia — TaskbarCreated

Quando Esplora risorse va in crash o si riavvia e ricostruisce la barra delle applicazioni, tutte le icone tray registrate scompaiono. La shell trasmette un messaggio TaskbarCreated a tutte le finestre di primo livello alla creazione della barra delle applicazioni, e la convenzione a livello Win32 è di ri-registrare la propria icona alla ricezione di questo messaggio.19

Il NotifyIcon di WinForms gestisce questo messaggio internamente e si ri-registra automaticamente, quindi normalmente non serve pensarci. Se si richiama Shell_NotifyIcon direttamente tramite P/Invoke (o si mantiene un’app nativa con un’icona nella tray), assicurarsi che questa logica di ri-registrazione sia presente. Il sintomo “l’icona scompare quando Esplora risorse si riavvia, ma il processo è ancora vivo” è quasi sempre esattamente questo problema.

5.3. Dimenticare Dispose lascia un’icona fantasma

A livello Win32, un’icona tray deve essere rimossa esplicitamente (NIM_DELETE) non appena non serve più, e in WinForms questa responsabilità ricade su NotifyIcon.Dispose().18 Assicurarsi che il percorso di uscita passi sempre per Visible = falseDispose(). Qualsiasi sviluppatore di app residenti ha visto almeno una volta questa scena: il processo termina in modo anomalo senza pulizia, e un’icona fantasma resta bloccata nella tray finché l’utente non ci passa sopra il puntatore del mouse.

Come nel codice del capitolo 3, è più sicuro pulire l’icona tray nel gestore del menu “Esci”, prima di chiamare Close().

6. Notifiche toast — quale API usare

Una notifica toast è il banner che compare nell’angolo in basso a destra dello schermo e poi resta nel centro notifiche. Il modo di inviarne una da un’app desktop è continuato a cambiare nell’ultimo decennio, e i risultati di ricerca sono ancora pieni di vecchi esempi. Ecco come stanno le cose a partire dal 2026.6

API NuGet / Namespace Stato (a partire dal 2026) Verdetto
Windows App SDK AppNotification Microsoft.WindowsAppSDK / Microsoft.Windows.AppNotifications Ufficialmente raccomandata per WinForms, WPF e Win32 non pacchettizzato Da usare per qualsiasi cosa nuova
Community Toolkit Notifications Microsoft.Toolkit.Uwp.Notifications Archiviato. Non fa più parte dell’attuale Community Toolkit; ultimo aggiornamento nel 2022 Solo per mantenere codice esistente. Non adottarlo per nuovo lavoro
Uso diretto di WinRT Windows.UI.Notifications (tramite il TFM Windows) Solo manutenzione. Raccomandato principalmente per UWP Non adottarlo per nuovo lavoro
NotifyIcon.ShowBalloonTip System.Windows.Forms API di notifica a fumetto. Renderizzata come toast su Windows 10; su Windows 11 è una visualizzazione temporanea che non persiste nel centro notifiche Va bene per casi semplici, ma occorre notare che il comportamento cambia tra le versioni del sistema operativo

Due note aggiuntive.

  • ShowBalloonTip appartiene a quei casi “API vecchia, ma in realtà ancora viva”. Su Windows 10, i fumetti vengono renderizzati come toast e restano nel centro notifiche finché non vengono chiusi. Su Windows 11, invece, si comportano più come un fumetto temporaneo tradizionale e non persistono nel centro notifiche una volta scomparsi. Anche la durata di visualizzazione (timeout) viene ora ignorata, seguendo invece le impostazioni di accessibilità del sistema operativo. Questo è più che sufficiente per un’app residente in tray che vuole solo dare un avviso leggero su un cambio di stato, ma se si vuole che una cronologia persista nel centro notifiche, tenere presente che su Windows 11 non aiuterà.20
  • L’approccio classico di usare Windows.UI.Notifications puro da un’app desktop ha un vincolo ben noto: il toast non verrà visualizzato a meno che non si sia registrato un collegamento con un AUMID (AppUserModelID) nel menu Start.21 L’AppNotification trattata nel prossimo capitolo è proprio ciò che elimina questa scomodità.

7. Un’implementazione minima di Windows App SDK AppNotification

L’API di notifiche delle app del Windows App SDK funziona anche da un exe semplice e non pacchettizzato, senza identità di pacchetto.8 I prerequisiti lato progetto sono un TFM specifico per Windows (ad esempio net8.0-windows10.0.19041.0), <WindowsPackageType>None</WindowsPackageType> (per il caso non pacchettizzato), e avere il runtime del Windows App SDK installato sulla destinazione di distribuzione.9

using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;

// Una sola volta all'avvio; l'ordine registrazione handler → Register() è importante
AppNotificationManager.Default.NotificationInvoked += (_, args) =>
{
    // I clic sul toast e le pressioni dei pulsanti arrivano qui (distinguerli tramite args.Arguments)
    if (args.Arguments.TryGetValue("action", out var action) && action == "openLog")
    {
        ShowLogWindow();
    }
};
AppNotificationManager.Default.Register();

// Mostra la notifica
var notification = new AppNotificationBuilder()
    .AddText("Importazione degli ordini completata")
    .AddText("12 nuovi, 1 errore")
    .AddButton(new AppNotificationButton("Apri registro")
        .AddArgument("action", "openLog"))
    .BuildNotification();
AppNotificationManager.Default.Show(notification);

I punti pratici a cui prestare attenzione:

  • Iscriversi a NotificationInvoked prima di chiamare Register(). Sbagliare questo ordine fa perdere l’evento quando un clic sul toast avvia l’app.9
  • Chiamare AppNotificationManager.Default.Unregister() prima che l’app esca. La documentazione ufficiale afferma esplicitamente che chiamare Unregister prima dell’uscita pulisce le risorse e garantisce che l’app si avvii correttamente la prossima volta che si clicca su una notifica. Integrare questo nel gestore del menu “Esci” del capitolo 3, insieme a NotifyIcon.Dispose() (esiste anche un UnregisterAll() separato per casi come la disinstallazione di un’app che non userà mai più le notifiche, il quale cancella completamente le informazioni di registrazione).22
  • Per le app non pacchettizzate, Register() gestisce automaticamente la registrazione del server COM e il lavoro equivalente all’AUMID. Non serve più la configurazione del collegamento né la registrazione nel registro di sistema richieste dalla generazione precedente, quella del WinRT puro. Se invece si pacchettizza con MSIX, occorrerà dichiarare l’attivatore COM in Package.appxmanifest.9
  • Fare attenzione se si sceglie una distribuzione self-contained che include il runtime del Windows App SDK. AppNotificationManager dipende dal pacchetto Singleton del Windows App SDK, ed esistono considerazioni documentate ufficialmente per richiamarlo da un’app self-contained. Verificare questo aspetto prima di stabilire il metodo di distribuzione.23
  • Le notifiche da processi elevati (amministratore) non sono supportate. Show non genera un’eccezione, fallisce semplicemente in silenzio, il che rende la causa radice difficile da notare. Per le app che richiedono l’elevazione, considerare di isolare solo la notifica in un processo non elevato (questo approccio di isolamento è trattato in “Un modo concreto per isolare solo le parti di un’app Windows che richiedono privilegi di amministratore”).10
  • Quando si clicca su un toast, se l’app non è attualmente in esecuzione, verrà avviato un nuovo processo. In un’app residente in tray, occorre combinare questo con la prevenzione di istanze multiple (capitolo 3), in modo che il lavoro venga passato all’istanza esistente.

8. Utilizzo avanzato — toast con barra di avanzamento e pulizia delle notifiche

8.1. Aggiornare l’avanzamento dentro un toast — UpdateAsync

Per un’elaborazione lunga come “importazione di 120 record d’ordine”, l’approccio corretto non è ri-attivare una nuova notifica ripetutamente, ma aggiornare la barra di avanzamento dentro un singolo toast. AppNotification ha un meccanismo di data binding e aggiornamento costruito appositamente per questo.24

La costruzione avviene in tre fasi. Prima, trasformare ogni valore della barra di avanzamento in un segnaposto usando i metodi Bind, dare alla notifica un’identità tramite Tag (e Group, se necessario), e mostrarla. Dopodiché, basta passare un AppNotificationProgressData a UpdateAsync, e viene riscritto solo il contenuto del toast già visualizzato.

const string tag = "import-progress";
const string group = "batch";

// Visualizzazione iniziale: associa il valore e imposta il valore iniziale su Progress
var notification = new AppNotificationBuilder()
    .AddText("Importazione dei dati degli ordini in corso")
    .AddProgressBar(new AppNotificationProgressBar()
        .BindStatus().BindValue().BindValueStringOverride())
    .SetTag(tag)
    .SetGroup(group)
    .BuildNotification();

notification.Progress = new AppNotificationProgressData(1) // numero di sequenza
{
    Status = "Importazione in corso...", Value = 0, ValueStringOverride = "0/120 elementi"
};
AppNotificationManager.Default.Show(notification);

// A ogni avanzamento del progresso: incrementa il numero di sequenza e aggiorna
var data = new AppNotificationProgressData(2)
{
    Status = "Importazione in corso...", Value = 45 / 120.0, ValueStringOverride = "45/120 elementi"
};
await AppNotificationManager.Default.UpdateAsync(data, tag, group);

Il numero di sequenza è un intero diverso da zero che indica l’ordine di aggiornamento; se più aggiornamenti arrivano fuori ordine, vince quello con il numero più alto. Questo esiste affinché gli aggiornamenti concorrenti inviati da un’elaborazione asincrona non tornino indietro a un valore obsoleto, quindi assicurarsi che aumenti sempre in modo monotono.25

8.2. “Aggiornamento” contro “sostituzione” — quando usare quale

Si può anche aggiornare l’aspetto ri-mostrando (Show) una nuova notifica con lo stesso Tag — una “sostituzione” — ma il comportamento differisce da un “aggiornamento”.24

  Sostituzione (nuovo Show con lo stesso Tag) Aggiornamento (UpdateAsync)
Posizione nel centro notifiche Si sposta in alto Resta al suo posto
Cosa può essere modificato Tutto, incluso il layout Solo la barra di avanzamento associata e il testo principale
Popup Può comparire di nuovo Non compare di nuovo (riscritta silenziosamente)
Se l’utente l’aveva già chiusa Sempre consegnata Fallisce

L’indicazione ufficiale è chiara anche qui: aggiornamento per il progresso intermedio, sostituzione al completamento. Non serve far comparire una notifica ogni volta che l’avanzamento passa dal 50% al 65%, ma “importazione completata (3 errori)” dovrebbe raggiungere l’utente anche se ha già chiuso la notifica di avanzamento. In pratica, lo schema comune è rimuovere la barra di avanzamento al completamento e sostituirla con un layout diverso dotato di pulsanti, come “Visualizza dettagli errori”.

8.3. Pulire le vecchie notifiche rimaste nel centro notifiche

I toast si accumulano nel centro notifiche finché l’utente non li elimina. Se un’app residente invia diverse notifiche ogni giorno, il centro notifiche finisce per riempirsi di vecchie notifiche dell’app, finendo per seppellire quelle importanti.

Associare un Tag / Group permette di eliminare da codice le notifiche della propria app (per tag, per gruppo, o tutte). Si può anche impostare una scadenza. Vale la pena decidere le regole di pulizia — “eliminare le notifiche di tipo progresso al completamento”, “accorciare la scadenza per quelle informative” — nello stesso momento in cui si progetta come vengono inviate le notifiche.26

9. Progettare per i casi in cui una notifica “non arriva”

Una notifica toast non è un meccanismo di consegna garantito. Esistono percorsi del tutto normali in cui non viene mostrata o non viene notata.

Caso Comportamento
Assistente notifiche (non disturbare) attivo Il banner non compare; la notifica va direttamente nel centro notifiche
Notifiche disattivate, per singola app o per l’intero sistema Non compare nulla
AppNotification da un processo elevato Non supportata. Non compare nulla
È in esecuzione un’app a schermo intero, ecc. Il sistema operativo può sopprimerla a propria discrezione

Non disturbare si chiama “Assistente notifiche (Focus assist)” su Windows 10 ed è controllato tramite l’interruttore “Non disturbare” in “Impostazioni → Sistema → Notifiche” su Windows 11, e può essere gestito dall’utente o da un criterio di gruppo. Esiste persino una regola per cui viene attivato automaticamente per la prima ora dopo un aggiornamento del sistema operativo.11 L’assistente notifiche non significa “non inviare notifiche”, ma “sopprimere il banner e instradare direttamente al centro notifiche” — quindi l’utente la vedrà comunque se in seguito apre il centro notifiche.12

L’implicazione progettuale è semplice:

  • Trattare il toast come un canale supplementare che aiuta gli utenti a rispondere più velocemente se lo notano, e assicurarsi che le informazioni obbligatorie per il processo aziendale possano sempre essere confermate in una schermata o in un elenco all’interno dell’app. Non costruire un flusso di lavoro che presuppone che “il toast sia stato probabilmente mostrato”.
  • Per le notifiche che devono assolutamente interrompere l’utente (come un avviso di arresto di emergenza), esiste un meccanismo per superare il non disturbare con il consenso dell’utente tramite AppNotificationScenario.Urgent,27 ma un uso eccessivo porterà gli utenti a disattivare del tutto le notifiche dell’app. Riservarlo solo ai casi veramente urgenti.

Per la questione più ampia di come mostrare a schermo cose come errori di dispositivi esterni, vedere anche “Best practice per controllare e mostrare lo stato dei dispositivi esterni”.

10. Quando serve notificare da un servizio Windows

È un requisito naturale: “quando un servizio residente rileva un problema, voglio avvisare l’utente collegato con un toast” — ma un servizio non può inviare un toast direttamente. A partire da Windows Vista, i servizi vengono eseguiti in una sessione dedicata chiamata Sessione 0 e non hanno alcuna capacità di mostrare un’interfaccia utente che interagisca con un utente.13

L’approccio standard consiste nel separare l’interfaccia utente in un processo eseguito nella sessione dell’utente.

[Sessione 0]                       [Sessione utente]
 Servizio Windows   <-- IPC -->   Agente residente in tray
 (monitoraggio/logica applicativa)   named pipe, ecc.   (NotifyIcon + AppNotification)

Negli ambienti in cui più utenti accedono contemporaneamente (RDP, terminali condivisi), l’agente finisce per essere eseguito una volta per sessione. Decidere in anticipo, come requisito, “chi deve essere notificato” (tutti, o solo un utente specifico) rende la progettazione più stabile.

  • La residenza in tray si basa su NotifyIcon. Mostrarla tramite Icon + Visible = true, rispettare il limite di 127 caratteri per Text (.NET 6+), e assicurarsi che Dispose() venga chiamato in modo affidabile nel percorso di uscita. Usare l’icona e il tooltip anche come indicatore sempre visibile dello stato corrente.
  • “Resta nella tray anche quando chiuso con il pulsante ×” si implementa annullando FormClosing, ma annullare solo per CloseReason.UserClosing e fornire sempre un menu “Esci” esplicito.
  • Solo l’utente può decidere se un’icona sia sempre visibile. Gestire la scomparsa dell’icona ai riavvii di Esplora risorse ri-registrandola su TaskbarCreated (WinForms lo fa automaticamente).
  • Per qualsiasi nuovo sviluppo di notifiche toast, l’AppNotificationManager del Windows App SDK è l’unica vera scelta. Funziona anche da un exe non pacchettizzato senza identità di pacchetto, e Register() automatizza il tedioso lavoro di registrazione. Le notifiche del Community Toolkit sono archiviate, e il WinRT puro è solo in manutenzione.
  • Per il progresso intermedio di un’elaborazione lunga, riscrivere silenziosamente la barra di avanzamento dentro un singolo toast con UpdateAsync, e usare una sostituzione per far comparire una notifica al completamento — questo è lo schema standard. Associare un Tag/Group, e includere nella progettazione anche la pulizia delle vecchie notifiche.
  • Un toast è un canale supplementare che può non arrivare a causa del non disturbare o delle notifiche disattivate. Progettare in modo che le informazioni obbligatorie possano sempre essere confermate all’interno dell’app, e tenere presente che le notifiche da processi elevati non sono supportate.
  • Le notifiche da un servizio si realizzano tramite l’architettura di separazione: agente residente in tray sul lato sessione utente più IPC.

Articoli correlati

Aree di consulenza correlate

KomuraSoft LLC (合同会社小村ソフト) si occupa della progettazione e implementazione di strumenti di monitoraggio e agenti di notifica residenti in tray, della revisione dell’architettura di sistemi residenti combinati con servizi Windows, e della risoluzione dei problemi di app residenti esistenti.

Riferimenti

  1. Microsoft Learn, NotifyIcon Class / NotifyIcon.Visible Property. Sul fatto che NotifyIcon è il componente che mostra un’icona nell’area di notifica, sulla necessità di impostare la proprietà Icon, e sul fatto che Visible ha valore predefinito false.  2 3

  2. Microsoft Learn, NotifyIcon.Text Property. Sul limite del testo del tooltip e sull’ArgumentException generata al superamento.  2

  3. Microsoft Learn, Windows Forms Controls and Equivalent WPF Controls. Sull’assenza in WPF di un controllo equivalente a NotifyIcon.  2

  4. Microsoft Learn, MSBuild properties for .NET desktop projects. Sulla possibilità di abilitare insieme UseWPF e UseWindowsForms.  2

  5. Microsoft Learn, Form.FormClosing Event. Sul fatto che l’evento si attiva prima che il form si chiuda, e che Cancel permette di annullare la chiusura.  2

  6. Microsoft Learn, Windows notifications overview. Tabella delle API di notifica raccomandate per tipo di app, inclusa la raccomandazione del Windows App SDK per WinForms/WPF/Win32 non pacchettizzato, e lo stato “solo manutenzione” di Windows.UI.Notifications.  2

  7. Microsoft Learn, Windows Community Toolkit — Notifications (archive). Sull’archiviazione del componente Notifications, sostituito dalle notifiche delle app del Windows App SDK. 

  8. Microsoft Learn, Use the Windows App SDK in an existing project. Sul fatto che l’API di notifiche delle app del Windows App SDK supporta sia le app pacchettizzate sia quelle non pacchettizzate, senza richiedere un’identità di pacchetto.  2

  9. Microsoft Learn, Quickstart: App notifications in the Windows App SDK. Sull’uso di AppNotificationManager/AppNotificationBuilder, sulla registrazione automatica tramite Register() per le app non pacchettizzate, sulla necessità di iscriversi a NotificationInvoked prima di Register(), e sulle dichiarazioni del manifesto per le app pacchettizzate.  2 3 4

  10. Microsoft Learn, App notifications overview. Sul fatto che l’invio di notifiche da un’app elevata non è supportato.  2

  11. Microsoft Learn, Notifications don’t appear or aren’t displayed. Sulla soppressione delle notifiche tramite il non disturbare (Quiet Hours / Focus Assist), sull’attivazione automatica per la prima ora dopo un aggiornamento del sistema operativo, e sui passaggi di verifica quando le notifiche non vengono visualizzate.  2

  12. Microsoft Learn, Focus assist. Sul fatto che l’assistente notifiche sopprime la visualizzazione del banner della notifica e la instrada direttamente al centro notifiche.  2

  13. Microsoft Learn, Service Changes for Windows Vista / Interactive Services. Sul fatto che la Sessione 0 è dedicata ai servizi e non supporta l’interazione con un utente, e sulla raccomandazione di usare CreateProcessAsUser per creare un processo nella sessione dell’utente e coordinarsi tramite IPC per mostrare qualcosa all’utente.  2

  14. Microsoft Learn, NotifyIcon.Text maximum text length increased. Sulla modifica non retrocompatibile in .NET 6 che ha esteso la lunghezza massima di Text da 63 a 127 caratteri. 

  15. Microsoft Learn, How to associate a shortcut menu with a Windows Forms NotifyIcon component. Sull’associazione di un menu al clic destro tramite ContextMenuStrip. 

  16. Microsoft Learn, FormClosingEventArgs Class. Sulla possibilità di determinare il motivo della chiusura del form tramite CloseReason. 

  17. Microsoft Learn, How to close a window or dialog box (WPF). Sullo schema dell’evento Closing di WPF: impostare e.Cancel = true e chiamare Hide() per nascondere invece di chiudere. 

  18. Microsoft Learn, Notification Area (Windows Shell). Sul fatto che le icone vengono aggiunte per default all’area di overflow, che solo l’utente può promuovere un’icona all’area di notifica, e sulla necessità di rimuovere le icone non più necessarie tramite NIM_DELETE.  2

  19. Microsoft Learn, The Taskbar. Sulla trasmissione del messaggio TaskbarCreated alla creazione della barra delle applicazioni, e sulla necessità di ri-registrare la propria icona tray alla ricezione. 

  20. Microsoft Learn, Shell_NotifyIconW function. Sul fatto che le notifiche a fumetto vengono renderizzate come toast (banner) e persistono nel centro notifiche su Windows 10, sono una visualizzazione temporanea che non persiste su Windows 11, e la durata di visualizzazione segue le impostazioni di accessibilità. 

  21. Microsoft Learn, Send a local toast notification from a C# app (Win32/API classica). Sul requisito di registrare un collegamento con AUMID nel menu Start quando si usa il ToastNotificationManager puro. 

  22. Microsoft Learn, AppNotificationManager.Register Method. Sulla necessità di chiamare Unregister prima che l’app esca per pulire le risorse e garantire che i successivi avvii da notifica funzionino correttamente, e sull’uso di UnregisterAll per cancellare le informazioni di registrazione quando le notifiche non verranno più usate. 

  23. Microsoft Learn, AppNotificationManager.UpdateAsync Method / Windows App SDK deployment architecture. Sul fatto che la classe AppNotificationManager dipende dal pacchetto Singleton del Windows App SDK, e sulle considerazioni per richiamarla da un’app self-contained. 

  24. Microsoft Learn, App notification progress bar and data binding. Sul data binding tramite i metodi Bind, sull’aggiornamento tramite AppNotificationProgressData e UpdateAsync, e sulle differenze tra aggiornamento e sostituzione (posizione nel centro notifiche, cosa può essere modificato, ricomparsa del popup, e comportamento dopo che l’utente l’ha chiusa), e sul fatto che al completamento andrebbe considerata una sostituzione.  2

  25. Microsoft Learn, AppNotificationProgressData(UInt32) Constructor. Sul fatto che il numero di sequenza è un intero diverso da zero che specifica l’ordine di aggiornamento, con i dati dal numero più alto mostrati quando si ricevono più aggiornamenti. 

  26. Microsoft Learn, Remove app notifications. Sull’associazione di tag/gruppi, sulla rimozione delle notifiche già mostrate, e sull’impostazione della scadenza. 

  27. Microsoft Learn, App notification content. Sulla priorità delle notifiche importanti con scenario=”urgent” rispetto alle impostazioni di non disturbare, con il consenso dell’utente. 

Articoli recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

Domande frequenti

Domande che ricorrono nelle consulenze sull’argomento dell’articolo.

Come si crea un'app residente nella system tray con WPF?
WPF non dispone di alcun controllo equivalente a NotifyIcon, quindi la configurazione minima consiste nell'abilitare sia UseWPF sia UseWindowsForms nel csproj e usare il NotifyIcon di WinForms. Se si desidera uno stile di scrittura compatibile con XAML, una libreria di terze parti come H.NotifyIcon è un'opzione, ma occorre tenere presente che non è un componente ufficiale Microsoft prima di decidere se adottarla.
Quale API bisogna usare per le notifiche toast?
Per qualsiasi nuovo sviluppo a partire dal 2026, sia che si tratti di un'app WinForms, WPF o non pacchettizzata, l'AppNotificationManager del Windows App SDK è la soluzione ufficialmente raccomandata. Microsoft.Toolkit.Uwp.Notifications è archiviato e non fa più parte dell'attuale Community Toolkit, mentre il ToastNotificationManager di WinRT è ora in stato di sola manutenzione. Se il codice esistente funziona già con uno di questi due, non c'è fretta di riscriverlo, ma non c'è motivo di sceglierli per un nuovo progetto.
Cosa bisogna controllare quando una notifica toast non viene visualizzata?
Si parte dalle impostazioni del sistema operativo. Se l'assistente notifiche (non disturbare) è attivo, le notifiche non vengono mostrate come banner ma vanno direttamente nel centro notifiche; inoltre le notifiche possono essere disattivate per singola app o per l'intero sistema nell'app Impostazioni. Sul fronte dell'implementazione, le due cause più comuni sono che AppNotification non è supportata quando viene inviata da un processo elevato (amministratore), e che Windows.UI.Notifications puro non mostra nulla se richiamato da un'app desktop priva di AUMID.
Un servizio Windows può inviare notifiche toast?
No, non può. A partire da Windows Vista, i servizi vengono eseguiti nella Sessione 0 e non hanno alcuna capacità di mostrare un'interfaccia utente per interagire con l'utente. Se occorre notificare l'utente, lo schema standard consiste nel collocare un processo agente residente nella system tray all'interno della sessione utente e farlo comunicare con il servizio tramite IPC, come le named pipe, mentre l'agente stesso si occupa di mostrare la notifica.

Profilo dell’autore

Pagina di presentazione dell’autore dell’articolo.

Go Komura

Rappresentante di KomuraSoft LLC

Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.

Torna al blog