Iconos de la bandeja del sistema y notificaciones toast en aplicaciones Windows — los escollos de NotifyIcon y cómo elegir el AppNotification adecuado
· Go Komura · Bandeja del sistema, NotifyIcon, Notificaciones toast, AppNotification, Windows App SDK, WinForms, WPF, C#, .NET, Aplicación residente, Desarrollo Windows, Consultoría técnica
«Quiero avisar discretamente al usuario cuando termine un proceso.» «Quiero que la supervisión siga funcionando incluso después de cerrar la ventana.» Estos son dos requisitos clásicos que suelen aparecer juntos en las aplicaciones empresariales: permanecer residente en la bandeja del sistema (system tray) y enviar notificaciones toast. Pero en cuanto se pasa a la implementación real, empiezan a surgir pequeños problemas uno tras otro: una aplicación que debería cerrarse en realidad no termina, el icono desaparece tras reiniciar el Explorador, o un toast que funcionaba perfectamente durante el desarrollo nunca aparece en las instalaciones del cliente.
Además, las notificaciones toast son un área en la que la API no ha dejado de cambiar de generación desde la época de UWP. No es raro buscar en línea y encontrar código de ejemplo que, según los estándares actuales, utiliza una API que ahora está, en el mejor de los casos, en solo mantenimiento. Este artículo organiza tanto el diseño de la residencia en la bandeja con NotifyIcon como la elección de una API de notificaciones toast a partir de 2026, junto con los escollos prácticos que probablemente encontrará.
1. Lo esencial primero
- La base de la residencia en la bandeja sigue siendo
System.Windows.Forms.NotifyIcon. No aparece a menos que establezcaIcony pongaVisible = true(el valor predeterminado esfalse). El texto de información sobre herramientasTextpuede llegar hasta 127 caracteres a partir de .NET 6 (63 caracteres antes de eso).12 - WPF no tiene ningún control equivalente a NotifyIcon. La tabla de comparación oficial indica explícitamente «Ningún control equivalente». Habilitar tanto
UseWPFcomoUseWindowsFormsen el csproj y usar el NotifyIcon de WinForms es la configuración mínima.34 - «Mantener la aplicación en la bandeja incluso cuando el usuario hace clic en el botón de cerrar» se implementa estableciendo
e.Cancel = trueenFormClosingy llamando aHide(). Proporcione siempre una forma explícita y separada de salir (un elemento «Salir» en el menú de la bandeja) y compruebeCloseReasonpara no bloquear nunca un apagado del sistema operativo.5 - La API de toast recomendada es clara a partir de 2026. Independientemente de si está construyendo una aplicación WinForms, WPF o no empaquetada, se recomienda el
AppNotificationManagerde Windows App SDK (NuGet: Microsoft.WindowsAppSDK).Windows.UI.Notificationsestá «solo en mantenimiento», y el componente de notificaciones del Community Toolkit está archivado.67 - El AppNotification de Windows App SDK funciona incluso desde un exe sencillo sin identidad de paquete.
AppNotificationManager.Default.Register()gestiona automáticamente el registro del servidor COM y el trabajo equivalente al AUMID, por lo que ya no necesita configurar un acceso directo en el menú Inicio como requerían las generaciones anteriores. Dicho esto, las notificaciones desde procesos elevados (administrador) no son compatibles.8910 - Un toast no es una forma garantizada de llegar al usuario. Mientras el asistente de enfoque (no molestar) está activo, el banner no aparece y la notificación va directamente al centro de notificaciones, y el usuario también puede desactivar las notificaciones por completo desde la configuración. Su diseño no debe suponer que «el toast se mostró» como condición previa de un flujo de trabajo empresarial.1112
- Un servicio de Windows no puede enviar toasts (la sesión 0 no tiene interfaz de usuario). El enfoque estándar consiste en colocar un agente residente en la bandeja dentro de la sesión del usuario y coordinarse mediante IPC.13
2. Fundamentos de la residencia en la bandeja con NotifyIcon
System.Windows.Forms.NotifyIcon es el componente que coloca un icono en el área de notificación situada en el extremo derecho de la barra de tareas. Las aplicaciones que siguen ejecutándose en segundo plano lo usan como punto de entrada para mostrar el estado y un menú de operaciones.1
private NotifyIcon _trayIcon = null!;
private void InitializeTrayIcon()
{
var menu = new ContextMenuStrip();
menu.Items.Add("Abrir", null, (_, _) => ShowMainWindow());
menu.Items.Add(new ToolStripSeparator());
menu.Items.Add("Salir", null, (_, _) => ExitApplication());
_trayIcon = new NotifyIcon
{
Icon = Properties.Resources.AppIcon, // El icono es obligatorio; sin él no se muestra nada
Text = "Herramienta de supervisión de pedidos ── en ejecución", // Información sobre herramientas (máx. 127 caracteres en .NET 6+)
ContextMenuStrip = menu,
Visible = true // El valor predeterminado es false; solo se muestra al establecerlo en true
};
_trayIcon.DoubleClick += (_, _) => ShowMainWindow();
}
Hay tres puntos que merece la pena recordar.
- El icono solo aparece cuando se establecen a la vez
IconyVisible = true. El valor predeterminado deVisibleesfalse.1 Texttiene un límite de longitud. Es de un máximo de 127 caracteres a partir de .NET 6, y de 63 caracteres para .NET 5 y anteriores, así como para .NET Framework; superarlo lanza unaArgumentException. Meter demasiada información de estado en la información sobre herramientas es una buena forma de que su aplicación acabe lanzando de repente una excepción en algún momento.214- Asocie un menú de clic derecho mediante
ContextMenuStrip. El icono de la bandeja suele ser el único punto de entrada visible para el usuario, así que proporcione como mínimo un elemento «Abrir» y otro «Salir».15
Otra cosa: el icono de la bandeja no es solo un punto de entrada para las notificaciones, también es un indicador de estado siempre visible. Dado que Icon y Text pueden intercambiarse mientras la aplicación se está ejecutando, reflejar el estado actual — un icono normal cuando todo va bien, un icono de advertencia cuando hay un problema, mostrar la hora del último procesamiento en la información sobre herramientas — permite al usuario echar un vistazo al icono y conocer el estado actual aunque se haya perdido un toast. El toast es el canal para «algo acaba de ocurrir en este momento», mientras que el icono de la bandeja es el canal para «este es el estado actual» — esa es la división de funciones.
3. Diseñar «permanecer en la bandeja al cerrar» y una ruta de salida correcta
Un comportamiento clásico de las aplicaciones residentes es: hacer clic en el botón de cerrar no termina el proceso, simplemente lo deja en la bandeja. La implementación en sí es fácil de escribir cancelando FormClosing, pero escribirla sin cuidado lleva a dos tipos de problemas: ya no existe ninguna forma de salir de la aplicación, y se bloquea el apagado de Windows.
private bool _exitRequested;
protected override void OnFormClosing(FormClosingEventArgs e)
{
// Si el usuario solo hizo clic en la X, ocultar en lugar de cerrar
if (!_exitRequested && e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
return;
}
// Cierre normal mediante el menú «Salir», apagado del sistema operativo, etc.
base.OnFormClosing(e);
}
private void ExitApplication()
{
_exitRequested = true;
_trayIcon.Visible = false;
_trayIcon.Dispose();
Close();
}
FormClosing se dispara antes de que se cierre el formulario, y establecer e.Cancel = true cancela el cierre. En ese momento, compruebe siempre CloseReason. El comportamiento correcto es cancelar solo en el caso de UserClosing (una acción del usuario), y no cancelar nunca en un cierre desencadenado por el sistema operativo como WindowsShutDown.516
WPF sigue la misma lógica: la propia documentación oficial describe el patrón de establecer e.Cancel = true en Window.Closing y llamar a Hide().17
Cabe señalar que, por su naturaleza, una aplicación residente siempre corre el riesgo de que «si se inicia dos veces, el mismo evento se procese dos veces». Tratamos la prevención de inicios múltiples en «Evitar el inicio múltiple de una aplicación Windows — mutex con nombre y activación de la instancia en ejecución en un segundo inicio». Y «Por qué usar .NET Generic Host y BackgroundService en una aplicación de escritorio» es la base para construir el contenido real de una aplicación residente — el bucle de supervisión o el procesamiento periódico.
4. Residencia en la bandeja en una aplicación WPF
WPF no tiene ningún control equivalente a NotifyIcon. Se trata de una carencia documentada de la plataforma, indicada explícitamente como «No equivalent control» en la tabla de comparación oficial «controles WPF equivalentes a los controles de WinForms».3
Existen dos opciones realistas.
| Opción | Resumen | Criterio |
|---|---|---|
| Usar el NotifyIcon de WinForms junto con WPF | Habilitar tanto UseWPF como UseWindowsForms en el csproj |
Sin dependencias adicionales. Suficiente si solo se necesita un icono y un menú |
| Terceros (por ejemplo, H.NotifyIcon) | Implementaciones de iconos de bandeja diseñadas para WPF/WinUI | Elegir si se quiere escribir en XAML o se necesita un popup más elaborado. Tener en cuenta que no es un componente oficial de Microsoft |
La primera opción consiste simplemente en enumerar dos propiedades una junto a la otra en un archivo de proyecto de estilo SDK.4
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
Después puede simplemente instanciar un System.Windows.Forms.NotifyIcon directamente desde una Application de WPF. Solo el menú termina siendo de WinForms (ContextMenuStrip), pero para algo tan sencillo como un menú de bandeja, esto rara vez supone un problema práctico.
5. Tres escollos de los iconos de la bandeja
5.1. Los iconos van por defecto a los «iconos ocultos» — no se puede forzar su visualización
La petición de mantener el icono de su aplicación siempre visible en la barra de tareas siempre acaba surgiendo, pero esto no se puede lograr mediante programación. La documentación oficial establece claramente que los iconos se agregan por defecto al área de desbordamiento (iconos ocultos), y que solo el usuario puede decidir si un icono se promociona para mostrarse siempre en el área de notificación.18
En Windows 11, el usuario activa o desactiva esto por aplicación en «Configuración → Personalización → Barra de tareas → Otros iconos de la bandeja del sistema». Si la visibilidad permanente es operativamente importante, lo correcto es documentarlo en su guía de instalación o ayuda («active esta configuración»), no intentar forzarlo en el código.
5.2. El icono desaparece cuando el Explorador se reinicia — TaskbarCreated
Cuando el Explorador se bloquea o se reinicia y reconstruye la barra de tareas, todos los iconos de bandeja registrados desaparecen. El shell transmite un mensaje TaskbarCreated a todas las ventanas de nivel superior al crear la barra de tareas, y la convención a nivel de Win32 es volver a registrar su icono al recibir este mensaje.19
El NotifyIcon de WinForms gestiona este mensaje internamente y se vuelve a registrar automáticamente, por lo que normalmente no necesita pensar en ello. Si invoca Shell_NotifyIcon usted mismo mediante P/Invoke (o mantiene una aplicación nativa con un icono en la bandeja), asegúrese de que esta lógica de reregistro está implementada. El síntoma «el icono desaparece solo cuando el Explorador se reinicia, pero el proceso sigue vivo» es casi siempre exactamente este problema.
5.3. Olvidar Dispose deja un icono fantasma
A nivel de Win32, un icono de bandeja debe eliminarse explícitamente (NIM_DELETE) en cuanto ya no sea necesario, y en WinForms esa responsabilidad recae en NotifyIcon.Dispose().18 Asegúrese de que su ruta de salida siempre pase por Visible = false → Dispose(). Cualquier desarrollador de aplicaciones residentes ha visto esta escena al menos una vez: el proceso termina de forma anómala sin limpieza, y un icono fantasma queda atascado en la bandeja hasta que el usuario pasa el cursor del ratón sobre él.
Como en el código del capítulo 3, es más seguro limpiar el icono de la bandeja en el controlador del menú «Salir», antes de llamar a Close().
6. Notificaciones toast — qué API usar
Una notificación toast es el banner que aparece en la esquina inferior derecha de la pantalla y luego permanece en el centro de notificaciones. La forma de enviar una desde una aplicación de escritorio no ha dejado de cambiar durante la última década, y los resultados de búsqueda todavía están llenos de ejemplos antiguos. Así están las cosas a partir de 2026.6
| API | NuGet / Espacio de nombres | Estado (a partir de 2026) | Veredicto |
|---|---|---|---|
| Windows App SDK AppNotification | Microsoft.WindowsAppSDK / Microsoft.Windows.AppNotifications |
Oficialmente recomendado para WinForms, WPF y Win32 no empaquetado | Usar para cualquier desarrollo nuevo |
| Community Toolkit Notifications | Microsoft.Toolkit.Uwp.Notifications | Archivado. Ya no forma parte del Community Toolkit actual; última actualización en 2022 | Solo para mantener código existente. No adoptar en proyectos nuevos |
| Uso directo de WinRT | Windows.UI.Notifications (a través del TFM de Windows) |
Solo mantenimiento. Recomendado principalmente para UWP | No adoptar en proyectos nuevos |
| NotifyIcon.ShowBalloonTip | System.Windows.Forms |
API de notificaciones tipo globo. Se representa como un toast en Windows 10; en Windows 11 es una visualización temporal que no persiste en el centro de notificaciones | Adecuada para casos sencillos, pero tenga en cuenta que su comportamiento cambia entre versiones del sistema operativo |
Dos notas adicionales.
ShowBalloonTippertenece a esos casos de «API antigua, pero en realidad sigue viva». En Windows 10, los globos se representan como un toast y permanecen en el centro de notificaciones hasta que se cierran. En Windows 11, en cambio, se comportan más como un globo temporal tradicional y no persisten en el centro de notificaciones una vez que desaparecen. La duración de visualización (timeout) ahora también se ignora, y sigue en su lugar la configuración de accesibilidad del sistema operativo. Esto es más que suficiente para una aplicación residente en la bandeja que solo quiere avisar levemente de un cambio de estado, pero si quiere que un historial persista en el centro de notificaciones, tenga en cuenta que esto no le ayudará en Windows 11.20- El enfoque clásico de usar
Windows.UI.Notificationspuro desde una aplicación de escritorio tiene una restricción bien conocida: el toast no se mostrará a menos que haya registrado un acceso directo con un AUMID (AppUserModelID) en el menú Inicio.21 El AppNotification que se trata en el siguiente capítulo es precisamente lo que elimina esta molestia.
7. Una implementación mínima de Windows App SDK AppNotification
La API de notificaciones de aplicación de Windows App SDK funciona incluso desde un exe sencillo y no empaquetado, sin identidad de paquete.8 Los requisitos previos del lado del proyecto son un TFM específico de Windows (por ejemplo, net8.0-windows10.0.19041.0), <WindowsPackageType>None</WindowsPackageType> (para el caso no empaquetado), y tener el runtime de Windows App SDK instalado en el destino de despliegue.9
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
// Una sola vez al iniciar; el orden registro del controlador → Register() es importante
AppNotificationManager.Default.NotificationInvoked += (_, args) =>
{
// Los clics en el toast y las pulsaciones de botones llegan aquí (distinguir mediante args.Arguments)
if (args.Arguments.TryGetValue("action", out var action) && action == "openLog")
{
ShowLogWindow();
}
};
AppNotificationManager.Default.Register();
// Mostrar la notificación
var notification = new AppNotificationBuilder()
.AddText("Importación de pedidos completada")
.AddText("12 nuevos, 1 error")
.AddButton(new AppNotificationButton("Abrir registro")
.AddArgument("action", "openLog"))
.BuildNotification();
AppNotificationManager.Default.Show(notification);
Los puntos prácticos a los que hay que prestar atención:
- Suscríbase a
NotificationInvokedantes de llamar aRegister(). Equivoque este orden y se perderá el evento cuando un clic en el toast inicie la aplicación.9 - Llame a
AppNotificationManager.Default.Unregister()antes de que la aplicación se cierre. La documentación oficial establece explícitamente que llamar a Unregister antes de salir limpia los recursos y garantiza que la aplicación se inicie correctamente la próxima vez que se haga clic en una notificación. Integre esto en el controlador del menú «Salir» del capítulo 3, junto aNotifyIcon.Dispose()(también existe unUnregisterAll()independiente para casos como desinstalar una aplicación que nunca volverá a usar notificaciones, el cual borra por completo la información de registro).22 - Para las aplicaciones no empaquetadas,
Register()gestiona automáticamente el registro del servidor COM y el trabajo equivalente al AUMID. Ya no se necesita la configuración de accesos directos ni el registro en el Registro que exigía la generación anterior, la del WinRT puro. Si en cambio empaqueta con MSIX, necesitará declarar el activador COM en Package.appxmanifest.9 - Tenga cuidado si elige una implementación autónoma (self-contained) que incluya el runtime de Windows App SDK.
AppNotificationManagerdepende del paquete Singleton de Windows App SDK, y existen consideraciones documentadas oficialmente para invocarlo desde una aplicación autónoma. Compruebe esto antes de decidir el método de distribución.23 - Las notificaciones desde procesos elevados (administrador) no son compatibles.
Showno lanza una excepción, simplemente falla en silencio, lo que hace que la causa raíz sea difícil de detectar. Para las aplicaciones que necesitan elevación, considere aislar solo la notificación en un proceso no elevado (este enfoque de aislamiento se trata en «Una forma concreta de aislar en una aplicación Windows solo las partes que requieren privilegios de administrador»).10 - Cuando se hace clic en un toast, si la aplicación no se está ejecutando en ese momento, se iniciará un nuevo proceso. En una aplicación residente en la bandeja, esto debe combinarse con la prevención de instancias múltiples (capítulo 3), de modo que el trabajo se transfiera a la instancia existente.
8. Uso avanzado — toasts con barra de progreso y limpieza de notificaciones
8.1. Actualizar el progreso dentro de un toast — UpdateAsync
Para un proceso largo como «importando 120 registros de pedidos», el enfoque correcto no es volver a disparar una nueva notificación una y otra vez, sino actualizar la barra de progreso dentro de un único toast. AppNotification cuenta con un mecanismo de enlace de datos y actualización construido precisamente para esto.24
La construcción se realiza en tres etapas. Primero, convierta cada valor de la barra de progreso en un marcador de posición mediante los métodos Bind, dé al toast una identidad a través de Tag (y Group, si es necesario), y muéstrelo. Después de eso, basta con pasar un AppNotificationProgressData a UpdateAsync, y solo se reescribe el contenido del toast ya mostrado.
const string tag = "import-progress";
const string group = "batch";
// Visualización inicial: enlazar el valor y establecer el valor inicial en Progress
var notification = new AppNotificationBuilder()
.AddText("Importando datos de pedidos")
.AddProgressBar(new AppNotificationProgressBar()
.BindStatus().BindValue().BindValueStringOverride())
.SetTag(tag)
.SetGroup(group)
.BuildNotification();
notification.Progress = new AppNotificationProgressData(1) // número de secuencia
{
Status = "Importando...", Value = 0, ValueStringOverride = "0/120 elementos"
};
AppNotificationManager.Default.Show(notification);
// Cada vez que avanza el progreso: incrementar el número de secuencia y actualizar
var data = new AppNotificationProgressData(2)
{
Status = "Importando...", Value = 45 / 120.0, ValueStringOverride = "45/120 elementos"
};
await AppNotificationManager.Default.UpdateAsync(data, tag, group);
El número de secuencia es un entero distinto de cero que indica el orden de actualización; si llegan varias actualizaciones desordenadas, gana la que tenga el número más alto. Este mecanismo existe para que las actualizaciones concurrentes enviadas desde procesamiento asíncrono no retrocedan a un valor obsoleto, así que asegúrese de que siempre aumente de forma monótona.25
8.2. «Actualizar» frente a «reemplazar» — cuándo usar cada uno
También se puede actualizar la apariencia volviendo a mostrar (Show) una nueva notificación con el mismo Tag — un «reemplazo» — pero el comportamiento difiere del de una «actualización».24
| Reemplazo (nuevo Show con el mismo Tag) | Actualización (UpdateAsync) | |
|---|---|---|
| Posición en el centro de notificaciones | Se mueve a la parte superior | Permanece en su sitio |
| Qué se puede modificar | Todo, incluido el diseño | Solo la barra de progreso enlazada y el texto principal |
| Popup | Puede volver a aparecer | No vuelve a aparecer (se reescribe en silencio) |
| Si el usuario ya la había cerrado | Siempre se entrega | Falla |
La recomendación oficial también es clara aquí: actualización para el progreso intermedio, reemplazo al completarse. No es necesario mostrar una notificación emergente cada vez que el progreso pasa del 50 % al 65 %, pero «importación completada (3 errores)» debería llegar al usuario aunque ya haya cerrado la notificación de progreso. En la práctica, el patrón habitual es retirar la barra de progreso al completarse y reemplazarla por un diseño diferente con botones, como «Ver detalles del error».
8.3. Limpiar las notificaciones antiguas que quedan en el centro de notificaciones
Los toasts se acumulan en el centro de notificaciones hasta que el usuario los borra. Si una aplicación residente envía varias notificaciones cada día, el centro de notificaciones termina lleno de notificaciones antiguas de su aplicación, lo que puede acabar sepultando las notificaciones importantes.
Adjuntar un Tag / Group le permite eliminar mediante código las notificaciones de su propia aplicación (por etiqueta, por grupo, o todas). También puede establecer una caducidad. Vale la pena decidir sus reglas de limpieza — «borrar las notificaciones de tipo progreso al completarse», «acortar la caducidad de las informativas» — al mismo tiempo que diseña cómo se envían las notificaciones.26
9. Diseñar para los casos en que una notificación «no llega»
Una notificación toast no es un mecanismo de entrega garantizado. Existen rutas totalmente normales en las que no se muestra o no se advierte.
| Caso | Comportamiento |
|---|---|
| El asistente de enfoque (no molestar) está activado | El banner no aparece; la notificación va directamente al centro de notificaciones |
| Notificaciones desactivadas, por aplicación o para todo el sistema | No se muestra nada |
| AppNotification desde un proceso elevado | No compatible. No se muestra nada |
| Se está ejecutando una aplicación a pantalla completa, etc. | El sistema operativo puede suprimirla a su propio criterio |
El modo No molestar se llama «Asistente de enfoque (Focus assist)» en Windows 10 y se controla mediante el interruptor «No molestar» en «Configuración → Sistema → Notificaciones» en Windows 11, y puede regirlo el usuario o una directiva de grupo. Incluso hay una regla según la cual se activa automáticamente durante la primera hora tras una actualización del sistema operativo.11 El asistente de enfoque no significa «no enviar notificaciones», sino «suprimir el banner y enviarla directamente al centro de notificaciones» — por lo que el usuario la seguirá viendo si más tarde abre el centro de notificaciones.12
La implicación para el diseño es sencilla:
- Trate el toast como un canal complementario que ayuda a los usuarios a responder más rápido si se dan cuenta, y asegúrese de que la información obligatoria para el proceso empresarial siempre se pueda confirmar en una pantalla o lista dentro de la aplicación. No construya un flujo de trabajo que suponga que «el toast probablemente se mostró».
- Para las notificaciones que deben interrumpir absolutamente al usuario (como una alerta de parada de emergencia), existe un mecanismo para saltarse el modo No molestar con el consentimiento del usuario mediante
AppNotificationScenario.Urgent,27 pero un uso excesivo hará que los usuarios acaben desactivando por completo las notificaciones de su aplicación. Resérvelo solo para los casos verdaderamente urgentes.
Para la cuestión más amplia de cómo mostrar en pantalla cosas como los errores de dispositivos externos, consulte también «Buenas prácticas para comprobar y mostrar el estado de dispositivos externos».
10. Cuando necesita notificar desde un servicio de Windows
Es un requisito natural: «cuando un servicio residente detecta un problema, quiero avisar al usuario que ha iniciado sesión mediante un toast» — pero un servicio no puede enviar un toast directamente. Desde Windows Vista, los servicios se ejecutan en una sesión dedicada llamada sesión 0 y no tienen ninguna capacidad de mostrar una interfaz de usuario que interactúe con un usuario.13
El enfoque estándar consiste en separar la interfaz de usuario en un proceso que se ejecuta en la sesión del usuario.
[Sesión 0] [Sesión de usuario]
Servicio de Windows <-- IPC --> Agente residente en la bandeja
(supervisión/lógica de negocio) tuberías con nombre, etc. (NotifyIcon + AppNotification)
- El servicio se centra exclusivamente en la supervisión y la lógica de negocio, y envía al agente mediante IPC todo aquello sobre lo que necesita notificar.
- El agente es una aplicación residente en la bandeja (exactamente lo que describen los capítulos 2 a 8 de este artículo) que se inicia cuando el usuario inicia sesión, y muestra los eventos recibidos como toasts o cambios de icono.
- La elección de un mecanismo de IPC la tratamos en «Cómo elegir la comunicación entre procesos en Windows», la construcción del lado del servicio en «Cómo crear y operar un servicio de Windows», y el propio mecanismo de sesiones en «Cómo entender el aislamiento de sesiones de Windows — sesión 0, RDP y ejecución simultánea de varios usuarios».
En entornos donde varios usuarios inician sesión simultáneamente (RDP, terminales compartidos), el agente termina ejecutándose una vez por sesión. Decidir de antemano, como requisito, «a quién se debe notificar» (a todos, o solo a un usuario específico) hace que el diseño sea más estable.
11. Resumen
- La residencia en la bandeja se basa en
NotifyIcon. Muéstrelo medianteIcon+Visible = true, respete el límite de 127 caracteres paraText(.NET 6+), y asegúrese de queDispose()se invoque de forma fiable en la ruta de salida. Use también el icono y la información sobre herramientas como un indicador siempre visible del estado actual. - «Permanecer en la bandeja incluso al cerrar con el botón ×» se implementa cancelando
FormClosing, pero cancele solo en el caso deCloseReason.UserClosingy proporcione siempre un menú «Salir» explícito. - Solo el usuario puede decidir si un icono se muestra siempre. Gestione la desaparición del icono al reiniciar el Explorador volviendo a registrarlo en
TaskbarCreated(WinForms lo hace automáticamente). - Para cualquier desarrollo nuevo de notificaciones toast, el
AppNotificationManagerde Windows App SDK es la única opción real. Funciona incluso desde un exe no empaquetado sin identidad de paquete, yRegister()automatiza el tedioso trabajo de registro. Las notificaciones del Community Toolkit están archivadas, y el WinRT puro está solo en mantenimiento. - Para el progreso intermedio de un proceso largo, reescriba en silencio la barra de progreso dentro de un único toast con
UpdateAsync, y use un reemplazo para mostrar una notificación emergente al completarse — ese es el patrón estándar. Adjunte unTag/Group, e incluya en el diseño la limpieza de notificaciones antiguas. - Un toast es un canal complementario que puede no llegar debido al modo No molestar o a las notificaciones desactivadas. Diseñe de forma que la información obligatoria siempre se pueda confirmar dentro de la aplicación, y tenga en cuenta que las notificaciones desde procesos elevados no son compatibles.
- Las notificaciones desde un servicio se logran mediante la arquitectura de separación de un agente residente en la bandeja del lado de la sesión de usuario más IPC.
Artículos relacionados
- Evitar el inicio múltiple de una aplicación Windows — mutex con nombre y activación de la instancia en ejecución en un segundo inicio
- Cómo crear y operar un servicio de Windows — desde elegir entre un servicio y el Programador de tareas hasta convertir un BackgroundService en servicio
- Cómo elegir la comunicación entre procesos en Windows — tabla de decisión de tuberías con nombre / TCP / gRPC / memoria compartida / COM
- Cómo entender el aislamiento de sesiones de Windows — sesión 0, RDP y ejecución simultánea de varios usuarios
- Por qué usar .NET Generic Host y BackgroundService en una aplicación de escritorio
- Una forma concreta de aislar en una aplicación Windows solo las partes que requieren privilegios de administrador
- Buenas prácticas para comprobar y mostrar el estado de dispositivos externos — un diseño que no se conforma con «Conectado»
Áreas de consultoría relacionadas
KomuraSoft LLC (合同会社小村ソフト) se encarga del diseño e implementación de herramientas de supervisión y agentes de notificación residentes en la bandeja, la revisión de arquitectura de sistemas residentes combinados con servicios de Windows, y la resolución de problemas de aplicaciones residentes existentes.
Referencias
-
Microsoft Learn, NotifyIcon Class / NotifyIcon.Visible Property. Sobre que NotifyIcon es el componente que muestra un icono en el área de notificación, la necesidad de establecer la propiedad Icon, y que Visible tiene por defecto el valor false. ↩ ↩2 ↩3
-
Microsoft Learn, NotifyIcon.Text Property. Sobre el límite del texto de información sobre herramientas y la ArgumentException que se lanza al superarlo. ↩ ↩2
-
Microsoft Learn, Windows Forms Controls and Equivalent WPF Controls. Sobre la ausencia en WPF de un control equivalente a NotifyIcon. ↩ ↩2
-
Microsoft Learn, MSBuild properties for .NET desktop projects. Sobre la posibilidad de habilitar juntos UseWPF y UseWindowsForms. ↩ ↩2
-
Microsoft Learn, Form.FormClosing Event. Sobre que el evento se dispara antes de que se cierre el formulario, y que Cancel permite cancelar el cierre. ↩ ↩2
-
Microsoft Learn, Windows notifications overview. Tabla de la API de notificaciones recomendada según el tipo de aplicación, incluida la recomendación de Windows App SDK para WinForms/WPF/Win32 no empaquetado, y el estado «solo mantenimiento» de Windows.UI.Notifications. ↩ ↩2
-
Microsoft Learn, Windows Community Toolkit — Notifications (archive). Sobre el archivado del componente Notifications, sustituido por las notificaciones de aplicación de Windows App SDK. ↩
-
Microsoft Learn, Use the Windows App SDK in an existing project. Sobre que la API de notificaciones de aplicación de Windows App SDK es compatible tanto con aplicaciones empaquetadas como no empaquetadas, sin requerir identidad de paquete. ↩ ↩2
-
Microsoft Learn, Quickstart: App notifications in the Windows App SDK. Sobre el uso de AppNotificationManager/AppNotificationBuilder, el registro automático mediante Register() en aplicaciones no empaquetadas, la necesidad de suscribirse a NotificationInvoked antes de Register(), y las declaraciones de manifiesto para aplicaciones empaquetadas. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, App notifications overview. Sobre que el envío de notificaciones desde una aplicación elevada no es compatible. ↩ ↩2
-
Microsoft Learn, Notifications don’t appear or aren’t displayed. Sobre la supresión de notificaciones mediante el modo No molestar (Quiet Hours / Focus Assist), la activación automática durante la primera hora tras una actualización del sistema operativo, y los pasos de comprobación cuando las notificaciones no se muestran. ↩ ↩2
-
Microsoft Learn, Focus assist. Sobre que el asistente de enfoque suprime la visualización del banner de la notificación y la envía directamente al centro de notificaciones. ↩ ↩2
-
Microsoft Learn, Service Changes for Windows Vista / Interactive Services. Sobre que la sesión 0 está dedicada exclusivamente a los servicios y no admite la interacción con un usuario, y la recomendación de usar CreateProcessAsUser para crear un proceso en la sesión del usuario y coordinarse mediante IPC para mostrar algo al usuario. ↩ ↩2
-
Microsoft Learn, NotifyIcon.Text maximum text length increased. Sobre el cambio disruptivo en .NET 6 que amplió la longitud máxima de Text de 63 a 127 caracteres. ↩
-
Microsoft Learn, FormClosingEventArgs Class. Sobre la posibilidad de determinar el motivo del cierre del formulario mediante CloseReason. ↩
-
Microsoft Learn, How to close a window or dialog box (WPF). Sobre el patrón del evento Closing de WPF de establecer e.Cancel = true y llamar a Hide() para ocultar en lugar de cerrar. ↩
-
Microsoft Learn, Notification Area (Windows Shell). Sobre que los iconos se agregan por defecto al área de desbordamiento, que solo el usuario puede promocionar un icono al área de notificación, y la necesidad de eliminar los iconos innecesarios mediante NIM_DELETE. ↩ ↩2
-
Microsoft Learn, The Taskbar. Sobre la transmisión del mensaje TaskbarCreated al crearse la barra de tareas, y la necesidad de volver a registrar el icono de la bandeja al recibirlo. ↩
-
Microsoft Learn, Shell_NotifyIconW function. Sobre que las notificaciones tipo globo se muestran como un toast (banner) y persisten en el centro de notificaciones en Windows 10, son una visualización temporal que no persiste en Windows 11, y que la duración de visualización sigue la configuración de accesibilidad. ↩
-
Microsoft Learn, Send a local toast notification from a C# app (Win32/API clásica). Sobre el requisito de registrar un acceso directo con AUMID en el menú Inicio al usar el ToastNotificationManager puro. ↩
-
Microsoft Learn, AppNotificationManager.Register Method. Sobre la necesidad de llamar a Unregister antes de que la aplicación se cierre para limpiar los recursos y garantizar que los inicios posteriores desde una notificación funcionen correctamente, y el uso de UnregisterAll para borrar la información de registro cuando las notificaciones ya no se vayan a usar nunca más. ↩
-
Microsoft Learn, AppNotificationManager.UpdateAsync Method / Windows App SDK deployment architecture. Sobre que la clase AppNotificationManager depende del paquete Singleton de Windows App SDK, y las consideraciones para invocarla desde una aplicación autónoma (self-contained). ↩
-
Microsoft Learn, App notification progress bar and data binding. Sobre el enlace de datos mediante los métodos Bind, la actualización mediante AppNotificationProgressData y UpdateAsync, y las diferencias entre actualización y reemplazo (posición en el centro de notificaciones, qué se puede modificar, reaparición del popup, y comportamiento tras cerrar el usuario la notificación), y que al completarse debería considerarse un reemplazo. ↩ ↩2
-
Microsoft Learn, AppNotificationProgressData(UInt32) Constructor. Sobre que el número de secuencia es un entero distinto de cero que especifica el orden de actualización, mostrándose los datos con el número más alto cuando se reciben varias actualizaciones. ↩
-
Microsoft Learn, Remove app notifications. Sobre la asignación de etiquetas/grupos, la eliminación de notificaciones ya mostradas, y el establecimiento de una caducidad. ↩
-
Microsoft Learn, App notification content. Sobre la prioridad de las notificaciones importantes con scenario=”urgent” sobre la configuración de No molestar, con el consentimiento del usuario. ↩
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
¿Hasta cuándo seguirán funcionando las aplicaciones VB6? — Estado del soporte del runtime y un camino práctico hacia la migración a .NET
¿Hasta cuándo seguirán funcionando las aplicaciones VB6? Este artículo aclara la asimetría entre la política de soporte del runtime de VB...
Era japonesa, festivos y fechas de cierre en aplicaciones empresariales: diseño resiliente a los cambios de era, JapaneseCalendar y cálculo de días hábiles en la práctica
Mostrar «Reiwa 8» en un informe, calcular días hábiles excluyendo festivos, pagar al final del mes siguiente tras un cierre el día 20: el...
Internacionalización de aplicaciones WinForms/WPF: la práctica de resx, ensamblados satélite y cambio de cultura
Organizamos desde una perspectiva práctica la internacionalización de aplicaciones de escritorio Windows: la diferencia entre CurrentCult...
Impresión y salida PDF en aplicaciones empresariales Windows — cómo elegir System.Drawing.Printing / WPF / bibliotecas de informes
Organiza la impresión WinForms con PrintDocument, la impresión WPF con FlowDocument/FixedDocument y las alternativas de salida PDF en una...
Externalización y desarrollo por encargo de una app Windows: qué aclarar antes de solicitarlo
Antes de encargar la externalización o el desarrollo por encargo de una app Windows, estos son los puntos a aclarar: revisión de software...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Hilo de UI y temporizadores
Hilo de UI de WPF / WinForms, flujos asíncronos, Dispatcher y diseño de temporizadores.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Desarrollo de aplicaciones para Windows
Aplicaciones empresariales, integración de dispositivos y herramientas de comunicación, de los requisitos al desarrollo.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿Cómo se crea una aplicación residente en la bandeja del sistema con WPF?
- WPF no dispone de ningún control equivalente a NotifyIcon, por lo que la configuración mínima consiste en habilitar tanto UseWPF como UseWindowsForms en el csproj y usar el NotifyIcon de WinForms. Si se desea un estilo de escritura compatible con XAML, una biblioteca de terceros como H.NotifyIcon es una opción, pero conviene tener en cuenta que no es un componente oficial de Microsoft antes de decidir adoptarla.
- ¿Qué API debería usarse para las notificaciones toast?
- Para cualquier desarrollo nuevo a partir de 2026, ya se trate de una aplicación WinForms, WPF o no empaquetada, el AppNotificationManager de Windows App SDK es la solución oficialmente recomendada. Microsoft.Toolkit.Uwp.Notifications está archivado y ya no forma parte del Community Toolkit actual, mientras que el ToastNotificationManager de WinRT se encuentra ahora en estado de solo mantenimiento. Si el código existente ya funciona con alguno de estos dos, no hay prisa por reescribirlo, pero no hay razón para elegirlos en un proyecto nuevo.
- ¿Qué hay que comprobar cuando una notificación toast no se muestra?
- Empiece por la configuración del sistema operativo. Si el asistente de enfoque (no molestar) está activado, las notificaciones no se muestran como un banner y van directamente al centro de notificaciones; además, las notificaciones pueden desactivarse por aplicación o para todo el sistema desde la aplicación Configuración. En el lado de la implementación, las dos causas habituales son que AppNotification no es compatible cuando se envía desde un proceso elevado (administrador), y que Windows.UI.Notifications puro no muestra nada al invocarse desde una aplicación de escritorio sin AUMID.
- ¿Puede un servicio de Windows enviar notificaciones toast?
- No, no puede. Desde Windows Vista, los servicios se ejecutan en la sesión 0 y no tienen ninguna capacidad de mostrar una interfaz de usuario para interactuar con el usuario. Si es necesario notificar al usuario, el patrón estándar consiste en colocar un proceso agente residente en la bandeja del sistema dentro de la sesión del usuario, y hacer que se comunique con el servicio mediante IPC, como tuberías con nombre, siendo el propio agente el responsable de mostrar la notificación.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.
Enlaces públicos