System Tray Icons and Toast Notifications in Windows Apps — NotifyIcon Pitfalls and Choosing the Right AppNotification API

· · System Tray, NotifyIcon, Toast Notifications, AppNotification, Windows App SDK, WinForms, WPF, C#, .NET, Tray-Resident App, Windows Development, Technical Consulting

“I want to quietly let the user know when a job finishes.” “I want monitoring to keep running even after the window is closed.” These are two classic requirements that show up together in business applications: staying resident in the system tray (the notification area) and sending toast notifications. But once you actually implement them, small problems keep surfacing one after another — an app that was supposed to close won’t actually exit, the icon disappears after Explorer restarts, or a toast that worked fine during development never shows up at the customer’s site.

On top of that, toast notifications are an area where the API has kept changing generations since the UWP era. It’s not unusual to search online and find sample code that, by today’s standards, uses an API that’s now maintenance-only at best. This article organizes both the design of tray residency with NotifyIcon and how to choose a toast notification API as of 2026, together with the practical pitfalls you’re likely to hit.

1. Bottom Line Up Front

  • The foundation of tray residency is still System.Windows.Forms.NotifyIcon. It won’t appear unless you set Icon and set Visible = true (the default is false). The tooltip Text can be up to 127 characters on .NET 6 and later (63 characters before that).12
  • WPF has no control equivalent to NotifyIcon. The official comparison table explicitly states “No equivalent control.” Enabling both UseWPF and UseWindowsForms in your csproj and using the WinForms NotifyIcon is the minimal setup.34
  • “Keep the app in the tray even when the user clicks the close button” is implemented by setting e.Cancel = true in FormClosing and calling Hide(). Always provide a separate, explicit way to exit (an “Exit” item on the tray menu), and check CloseReason so you never block an OS shutdown.5
  • The recommended toast API is clear as of 2026. Regardless of whether you’re building WinForms, WPF, or an unpackaged app, the Windows App SDK’s AppNotificationManager (NuGet: Microsoft.WindowsAppSDK) is recommended. Windows.UI.Notifications is “maintenance only,” and the Community Toolkit’s notification component is archived.67
  • The Windows App SDK’s AppNotification works even from a plain exe with no package identity. AppNotificationManager.Default.Register() automatically handles COM server registration and AUMID-equivalent work, so you no longer need to set up a Start menu shortcut the way older generations required. That said, notifications from elevated (administrator) processes are unsupported.8910
  • A toast is not a guaranteed way to reach the user. While Focus Assist (do not disturb) is active, the banner doesn’t appear and the notification goes straight to the notification center, and the user can turn notifications off entirely in Settings. Your design must not assume “the toast was shown” as a precondition for a business workflow.1112
  • A Windows service cannot send toasts (Session 0 has no UI). The standard approach is to place a tray-resident agent in the user’s session and coordinate over IPC.13

2. NotifyIcon Basics for Tray Residency

System.Windows.Forms.NotifyIcon is the component that puts an icon in the notification area at the right end of the taskbar. Apps that keep running in the background use it as the entry point for status display and an operations menu.1

private NotifyIcon _trayIcon = null!;

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

    _trayIcon = new NotifyIcon
    {
        Icon = Properties.Resources.AppIcon, // Icon is required; without it, nothing is shown
        Text = "Order Monitor Tool ── Running",     // Tooltip (max 127 characters on .NET 6+)
        ContextMenuStrip = menu,
        Visible = true                        // Default is false; only shown once set to true
    };
    _trayIcon.DoubleClick += (_, _) => ShowMainWindow();
}

There are three things worth keeping in mind.

  • The icon only appears once both Icon and Visible = true are set. The default value of Visible is false.1
  • Text has a length limit. It’s a maximum of 127 characters on .NET 6 and later, and 63 characters on .NET 5 and earlier as well as .NET Framework; exceeding it throws an ArgumentException. Cramming too much status information into the tooltip is a good way to have your app suddenly throw an exception at some point down the road.214
  • Associate a right-click menu using ContextMenuStrip. The tray icon is often the only entry point users can see, so at a minimum provide an “Open” and an “Exit” item.15

One more thing: the tray icon is not only an entry point for notifications, it’s also an always-visible status indicator. Since Icon and Text can be swapped out while the app is running, reflecting the current state — a normal icon when everything is fine, a warning icon when something’s wrong, showing the last processed time in the tooltip — means the user can glance at the icon and know the current state even if they missed a toast. The toast is the channel for “something just happened at this moment,” while the tray icon is the channel for “here is the state right now” — that’s the division of labor.

3. Designing “Stays in the Tray When Closed” and a Proper Exit Path

A classic tray-resident behavior is: clicking the close button doesn’t terminate the process, it just stays in the tray. The implementation itself is easy to write by canceling FormClosing, but if you write it carelessly it leads to two kinds of trouble: there’s no longer any way to exit the app, and it blocks Windows shutdown.

private bool _exitRequested;

protected override void OnFormClosing(FormClosingEventArgs e)
{
    // If the user just clicked the X, hide instead of closing
    if (!_exitRequested && e.CloseReason == CloseReason.UserClosing)
    {
        e.Cancel = true;
        Hide();
        return;
    }
    // Close normally via the "Exit" menu, OS shutdown, etc.
    base.OnFormClosing(e);
}

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

FormClosing fires before the form closes, and setting e.Cancel = true cancels the close. When you do this, always check CloseReason. The correct behavior is to cancel only for UserClosing (a user action), and never cancel for OS-triggered closes such as WindowsShutDown.516

WPF follows the same idea: the official documentation itself describes the pattern of setting e.Cancel = true in Window.Closing and calling Hide().17

Note that, by nature, a tray-resident app is always at risk of the “if it launches twice, the same event gets processed twice” problem. We covered preventing multiple instances in “Preventing multiple launches of a Windows app — named mutexes and activating the running instance on a second launch.” And “Why use .NET Generic Host and BackgroundService in a desktop app” is the foundation for how to build the actual contents of a resident app — the monitoring loop or periodic work.

4. Tray Residency in a WPF App

WPF has no control equivalent to NotifyIcon. This is a documented gap in the platform, spelled out explicitly as “No equivalent control” in the official “WPF controls equivalent to WinForms controls” comparison table.3

There are two realistic options.

Option Overview Guidance
Use the WinForms NotifyIcon alongside WPF Enable both UseWPF and UseWindowsForms in your csproj No extra dependency. Sufficient if all you need is an icon and a menu
Third-party (e.g., H.NotifyIcon) Tray icon implementations built for WPF/WinUI Choose this if you want to author it in XAML or need a richer popup. Keep in mind it’s not an official Microsoft component

The former is just a matter of listing two properties side by side in an SDK-style project file.4

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

You can then just new up a System.Windows.Forms.NotifyIcon directly from a WPF Application. Only the menu ends up being WinForms (ContextMenuStrip), but for something as simple as a tray menu that’s rarely a practical problem.

5. Three Pitfalls of Tray Icons

5.1. Icons default to the “hidden icons” area — you can’t force them to always show

There’s always a request to keep your app’s icon permanently visible on the taskbar, but this can’t be done programmatically. The official documentation states plainly that icons are added to the overflow (hidden icons) area by default, and only the user can decide whether an icon is promoted to always show in the notification area.18

On Windows 11, the user turns this on or off per app under “Settings → Personalization → Taskbar → Other system tray icons.” If always-on visibility matters operationally, the correct move is to document it in your install guide or help content (“please turn on this setting”), not to try to force it in code.

5.2. The icon disappears when Explorer restarts — TaskbarCreated

When Explorer crashes or restarts and rebuilds the taskbar, any registered tray icons disappear. The shell broadcasts a TaskbarCreated message to every top-level window when the taskbar is created, and the Win32-level convention is that you should re-register your icon upon receiving it.19

WinForms’ NotifyIcon handles this message internally and re-registers automatically, so you normally don’t need to think about it. If you’re P/Invoking Shell_NotifyIcon yourself (or maintaining a native app with a tray icon), make sure that re-registration logic is in place. “The icon disappears when Explorer restarts, but the process is still alive” is almost always this exact issue.

5.3. Forgetting to Dispose leaves a ghost icon behind

At the Win32 level, a tray icon must be explicitly removed (NIM_DELETE) once it’s no longer needed, and in WinForms that responsibility falls to NotifyIcon.Dispose().18 Make sure your exit path reliably goes through Visible = falseDispose(). Any tray-app developer has seen the scene at least once: the process terminates abnormally without cleaning up, and a ghost icon lingers in the tray until you hover the mouse over it.

As in the code in Chapter 3, it’s safest to clean up the tray icon as part of the “Exit” menu handler before calling Close().

6. Toast Notifications — Which API Should You Use

A toast notification is the banner that shows up in the lower-right corner of the screen and then remains in the notification center. The way to send one from a desktop app has kept changing over the last decade, and search results are still full of old samples. Here’s how things stand as of 2026.6

API NuGet / Namespace Status (as of 2026) Verdict
Windows App SDK AppNotification Microsoft.WindowsAppSDK / Microsoft.Windows.AppNotifications Officially recommended for WinForms, WPF, and unpackaged Win32 Use this for anything new
Community Toolkit Notifications Microsoft.Toolkit.Uwp.Notifications Archived. Not part of the current Community Toolkit; last updated in 2022 Only for maintaining existing code. Don’t adopt for new work
Direct WinRT usage Windows.UI.Notifications (via the Windows TFM) Maintenance only. Recommended primarily for UWP Don’t adopt for new work
NotifyIcon.ShowBalloonTip System.Windows.Forms Balloon notification API. Rendered as a toast on Windows 10; on Windows 11 it’s a temporary display that doesn’t persist in the notification center Fine for simple cases, but note its behavior changes across OS versions

Two additional notes.

  • ShowBalloonTip is one of those “old API, but actually still alive” cases. On Windows 10, balloons render as a toast and stay in the notification center until dismissed. On Windows 11, however, they behave more like a traditional temporary balloon and don’t remain in the notification center once they disappear. The display duration (timeout) is now ignored as well, following the OS’s accessibility settings instead. This is plenty for a tray-resident app that just wants to give a lightweight nudge about a state change, but if you want a history to persist in the notification center, keep in mind it won’t help you on Windows 11.20
  • The classic approach of using plain Windows.UI.Notifications from a desktop app has a well-known constraint: the toast won’t display unless you’ve registered a shortcut with an AUMID (AppUserModelID) in the Start menu.21 The AppNotification covered in the next chapter is what eliminates that hassle.

7. A Minimal Implementation of Windows App SDK AppNotification

The Windows App SDK’s app notification API works even from a plain, unpackaged exe, without a package identity.8 The project-side prerequisites are a Windows-specific TFM (e.g., net8.0-windows10.0.19041.0), <WindowsPackageType>None</WindowsPackageType> (for the unpackaged case), and having the Windows App SDK runtime installed at the deployment target.9

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

// Once at startup; handler registration → Register() order matters
AppNotificationManager.Default.NotificationInvoked += (_, args) =>
{
    // Toast clicks and button presses arrive here (identify via args.Arguments)
    if (args.Arguments.TryGetValue("action", out var action) && action == "openLog")
    {
        ShowLogWindow();
    }
};
AppNotificationManager.Default.Register();

// Show the notification
var notification = new AppNotificationBuilder()
    .AddText("Order import complete")
    .AddText("12 new, 1 error")
    .AddButton(new AppNotificationButton("Open log")
        .AddArgument("action", "openLog"))
    .BuildNotification();
AppNotificationManager.Default.Show(notification);

The practical points to watch for:

  • Subscribe to NotificationInvoked before calling Register(). Get the order wrong and you’ll miss the event when a toast click launches the app.9
  • Call AppNotificationManager.Default.Unregister() before the app exits. The official documentation explicitly states that calling Unregister before exit cleans up resources and ensures the app launches correctly the next time a notification is clicked. Wire this into the “Exit” menu handler from Chapter 3, alongside NotifyIcon.Dispose() (there’s also a separate UnregisterAll() for cases like uninstalling an app that will never use notifications again, which clears the registration entirely).22
  • For unpackaged apps, Register() automatically handles COM server registration and AUMID-equivalent work. You no longer need the shortcut setup or registry registration that the older, plain-WinRT generation required. If you package with MSIX instead, you’ll need to declare the COM activator in Package.appxmanifest.9
  • Be careful if you choose self-contained deployment that bundles the Windows App SDK runtime. AppNotificationManager depends on the Windows App SDK’s Singleton package, and there are officially documented considerations for calling it from a self-contained app. Check this before you settle on a distribution method.23
  • Notifications from elevated (administrator) processes are unsupported. Show doesn’t throw an exception, it just silently fails, which makes the root cause easy to miss. For apps that need elevation, consider isolating just the notification into a non-elevated process (this isolation approach is covered in “A concrete way to isolate only the parts of a Windows app that require administrator privileges”).10
  • When a toast is clicked, if the app isn’t currently running, a new process will be launched. In a tray-resident app, you need to combine this with multiple-instance prevention (Chapter 3) so the work gets handed off to the existing instance instead.

8. Advanced Usage — Toasts with a Progress Bar and Cleaning Up Notifications

8.1. Updating progress inside a toast — UpdateAsync

For long-running work such as “importing 120 order records,” the right approach is not to re-fire a fresh notification over and over, but to update the progress bar inside a single toast. AppNotification has a data-binding and update mechanism built exactly for this.24

The construction happens in three stages. First, turn each progress bar value into a placeholder using the Bind methods, give the notification an identity via Tag (and Group, if needed), and display it. After that, all you do is pass an AppNotificationProgressData to UpdateAsync, and only the contents of the already-displayed toast get rewritten.

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

// Initial display: bind the value and set the initial value on Progress
var notification = new AppNotificationBuilder()
    .AddText("Importing order data")
    .AddProgressBar(new AppNotificationProgressBar()
        .BindStatus().BindValue().BindValueStringOverride())
    .SetTag(tag)
    .SetGroup(group)
    .BuildNotification();

notification.Progress = new AppNotificationProgressData(1) // sequence number
{
    Status = "Importing...", Value = 0, ValueStringOverride = "0/120 items"
};
AppNotificationManager.Default.Show(notification);

// Each time progress advances: increment the sequence number and update
var data = new AppNotificationProgressData(2)
{
    Status = "Importing...", Value = 45 / 120.0, ValueStringOverride = "45/120 items"
};
await AppNotificationManager.Default.UpdateAsync(data, tag, group);

The sequence number is a non-zero integer indicating update order; if multiple updates arrive out of order, whichever has the highest number wins. This exists so that concurrent updates fired from asynchronous work don’t roll back to a stale value, so make sure it always increases monotonically.25

8.2. “Update” vs. “Replace” — when to use which

You can also update the visual appearance by re-Showing a new notification with the same Tag — a “replace” — but the behavior differs from an “update.”.24

  Replace (re-Show with the same Tag) Update (UpdateAsync)
Position in the notification center Moves to the top Stays in place
What can be changed Everything, including the layout Only the bound progress bar and the leading text
Popup Can pop up again Doesn’t pop up again (rewritten silently)
If the user already dismissed it Always delivered Fails

The official guidance is clear here too: update for interim progress, replace at completion. There’s no need to pop up a notification every time progress ticks from 50% to 65%, but “import finished (3 errors)” should reach the user even if they already dismissed the progress notification. In practice, the common pattern is to drop the progress bar at completion and replace it with a different layout that has buttons attached, such as “View error details.”

8.3. Cleaning up stale notifications left in the notification center

Toasts pile up in the notification center until the user clears them. If a resident app sends several notifications every day, the notification center fills up with your app’s old notifications, and that can end up burying the important ones.

Attaching a Tag / Group lets you delete your app’s own notifications from code (by tag, by group, or all of them). You can also set an expiration. It’s worth deciding your cleanup rules — “clear progress-type notifications on completion,” “keep expirations short for informational ones” — at the same time you design how notifications are sent.26

9. Designing for the Cases Where a Notification “Doesn’t Arrive”

A toast notification is not a guaranteed delivery mechanism. There are entirely normal paths where it isn’t shown or isn’t noticed.

Case Behavior
Focus Assist (do not disturb) is enabled The banner isn’t shown; the notification goes straight to the notification center
Notifications are off, per-app or system-wide Nothing is shown
AppNotification from an elevated process Unsupported. Nothing is shown
A full-screen app is running, etc. The OS may suppress it at its own discretion

Do Not Disturb is called “Focus assist” on Windows 10 and controlled via the “Do not disturb” toggle under “Settings → System → Notifications” on Windows 11, and it can be governed by the user or by group policy. There’s even a rule that it’s automatically enabled for the first hour after an OS upgrade.11 Focus assist doesn’t mean “don’t send notifications” — it means “suppress the banner and route straight to the notification center” — so the user will still see it if they later open the notification center.12

The design implication is simple:

  • Treat a toast as a supplementary channel that helps users respond faster if they happen to notice it, and make sure information that’s mandatory for the business process can always be confirmed in a screen or list inside the app. Don’t build a workflow that assumes “the toast presumably showed up.”
  • For notifications that absolutely must interrupt (such as an emergency-stop warning), there’s a mechanism to break through Do Not Disturb with user consent via AppNotificationScenario.Urgent,27 but overuse will lead users to turn off your app’s notifications entirely. Reserve it for genuinely urgent cases only.

For the broader question of how to surface things like external device errors on screen, see “Best practices for checking and displaying external device status.”

10. When You Need to Notify from a Windows Service

It’s a natural requirement: “when a resident service detects a problem, I want to notify the logged-in user with a toast” — but a service cannot send a toast directly. Since Windows Vista, services run in a dedicated session called Session 0 and have no ability to show UI that interacts with a user.13

The standard approach is to split the UI out into a process running in the user’s session.

[Session 0]                        [User Session]
 Windows Service   <-- IPC -->   Tray-Resident Agent
 (monitoring/business logic)   named pipes, etc.   (NotifyIcon + AppNotification)

In environments where multiple users are logged in simultaneously (RDP, shared terminals), the agent ends up running once per session. Deciding up front, as a requirement, “who should be notified” (everyone, or only a specific user) keeps the design from wavering later.

11. Summary

  • Tray residency is built on NotifyIcon. Show it via Icon + Visible = true, keep Text within the 127-character limit (.NET 6+), and make sure Dispose() is reliably called on the exit path. Use the icon and tooltip as an always-visible indicator of the current state, too.
  • “Stay in the tray even when closed with the × button” is implemented by canceling FormClosing, but cancel only for CloseReason.UserClosing and always provide an explicit “Exit” menu.
  • Only the user can decide whether an icon is always visible. Handle the icon disappearing on Explorer restarts by re-registering on TaskbarCreated (WinForms does this automatically).
  • For new toast notification work, the Windows App SDK’s AppNotificationManager is the only real choice. It works even from an unpackaged exe with no package identity, and Register() automates the tedious registration work. Community Toolkit notifications are archived, and plain WinRT is maintenance-only.
  • For interim progress on long-running work, quietly rewrite the progress bar inside a single toast with UpdateAsync, and use a replace to pop up a notification at completion — that’s the standard pattern. Attach a Tag/Group, and include cleanup of old notifications in your design.
  • A toast is a supplementary channel that can fail to arrive due to Do Not Disturb or notifications being off. Design so that mandatory information can always be confirmed inside the app, and keep in mind that notifications from elevated processes are unsupported.
  • Notifications from a service are achieved through the split architecture of a tray-resident agent in the user’s session plus IPC.

KomuraSoft LLC handles the design and implementation of tray-resident monitoring tools and notification agents, architecture reviews for resident systems combined with Windows services, and troubleshooting of existing resident apps.

References

  1. Microsoft Learn, NotifyIcon Class / NotifyIcon.Visible Property. On NotifyIcon being the component that displays an icon in the notification area, the need to set the Icon property, and Visible defaulting to false.  2 3

  2. Microsoft Learn, NotifyIcon.Text Property. On the limit for the tooltip text and the ArgumentException thrown when it’s exceeded.  2

  3. Microsoft Learn, Windows Forms Controls and Equivalent WPF Controls. On WPF having no control equivalent to NotifyIcon.  2

  4. Microsoft Learn, MSBuild properties for .NET desktop projects. On being able to enable both UseWPF and UseWindowsForms together.  2

  5. Microsoft Learn, Form.FormClosing Event. On the event firing before the form closes, and Cancel letting you cancel the close.  2

  6. Microsoft Learn, Windows notifications overview. On the recommended notification API by app type, including the Windows App SDK recommendation for WinForms/WPF/unpackaged Win32, and Windows.UI.Notifications’ “maintenance only” status.  2

  7. Microsoft Learn, Windows Community Toolkit — Notifications (archive). On the Notifications component being archived and replaced by Windows App SDK app notifications. 

  8. Microsoft Learn, Use the Windows App SDK in an existing project. On the Windows App SDK’s app notification API supporting both packaged and unpackaged apps without requiring a package identity.  2

  9. Microsoft Learn, Quickstart: App notifications in the Windows App SDK. On using AppNotificationManager/AppNotificationBuilder, automatic registration via Register() for unpackaged apps, subscribing to NotificationInvoked before Register(), and manifest declarations for packaged apps.  2 3 4

  10. Microsoft Learn, App notifications overview. On sending notifications from an elevated app being unsupported.  2

  11. Microsoft Learn, Notifications don’t appear or aren’t displayed. On notification suppression via Do Not Disturb (Quiet Hours / Focus Assist), automatic activation for the first hour after an OS upgrade, and steps to check when notifications don’t appear.  2

  12. Microsoft Learn, Focus assist. On focus assist suppressing the notification banner and routing it directly to the notification center.  2

  13. Microsoft Learn, Service Changes for Windows Vista / Interactive Services. On Session 0 being dedicated to services and not supporting interaction with a user, and the recommendation to use CreateProcessAsUser to create a process in the user’s session and coordinate over IPC to display anything to the user.  2

  14. Microsoft Learn, NotifyIcon.Text maximum text length increased. On the .NET 6 breaking change that extended Text’s maximum length from 63 to 127 characters. 

  15. Microsoft Learn, How to associate a shortcut menu with a Windows Forms NotifyIcon component. On associating a right-click menu via ContextMenuStrip. 

  16. Microsoft Learn, FormClosingEventArgs Class. On CloseReason letting you determine why the form is closing. 

  17. Microsoft Learn, How to close a window or dialog box (WPF). On the WPF Closing-event pattern of setting e.Cancel = true and calling Hide() to hide instead of close. 

  18. Microsoft Learn, Notification Area (Windows Shell). On icons being added to the overflow area by default, only the user being able to promote an icon to the notification area, and the need to remove unneeded icons via NIM_DELETE.  2

  19. Microsoft Learn, The Taskbar. On the TaskbarCreated message being broadcast when the taskbar is created, and that you should re-register your tray icon upon receiving it. 

  20. Microsoft Learn, Shell_NotifyIconW function. On balloon notifications rendering as a toast (banner) and persisting in the notification center on Windows 10, being a temporary display that doesn’t persist on Windows 11, and the display duration following accessibility settings. 

  21. Microsoft Learn, Send a local toast notification from a C# app (Win32/classic API). On the requirement to register an AUMID-bearing shortcut in the Start menu when using plain ToastNotificationManager. 

  22. Microsoft Learn, AppNotificationManager.Register Method. On calling Unregister before the app exits to clean up resources and ensure subsequent notification launches work correctly, and using UnregisterAll to clear registration information when notifications will never be used again. 

  23. Microsoft Learn, AppNotificationManager.UpdateAsync Method / Windows App SDK deployment architecture. On the AppNotificationManager class depending on the Windows App SDK’s Singleton package, and the considerations for calling it from a self-contained app. 

  24. Microsoft Learn, App notification progress bar and data binding. On data binding via Bind methods, updating via AppNotificationProgressData and UpdateAsync, and the differences between update and replace (position in the notification center, what can be changed, popup re-display, and behavior once the user has dismissed it), and that a replace should be considered at completion.  2

  25. Microsoft Learn, AppNotificationProgressData(UInt32) Constructor. On the sequence number being a non-zero integer that specifies update order, with the highest-numbered data displayed when multiple updates are received. 

  26. Microsoft Learn, Remove app notifications. On attaching tags/groups, removing already-shown notifications, and setting expiration. 

  27. Microsoft Learn, App notification content. On scenario=”urgent” notifications taking priority over Do Not Disturb settings with user consent. 

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

This article connects naturally to the following service pages.

Frequently Asked Questions

Common questions about the topic of this article.

How do I build a tray-resident app in WPF?
WPF has no control equivalent to NotifyIcon, so the minimal setup is to enable both UseWPF and UseWindowsForms in your csproj and use the WinForms NotifyIcon. If you want an XAML-friendly authoring experience, a third-party library such as H.NotifyIcon is an option, but keep in mind it isn't an official Microsoft component when deciding whether to adopt it.
Which toast notification API should I use?
For any new implementation as of 2026, the Windows App SDK's AppNotificationManager is the officially recommended choice regardless of whether you're building WinForms, WPF, or an unpackaged app. Microsoft.Toolkit.Uwp.Notifications is archived and no longer part of the current Community Toolkit, and the WinRT ToastNotificationManager is positioned as maintenance-only. If your existing code already runs on one of those two, there's no need to rush a rewrite, but there's no reason to choose them for new work.
What should I check when a toast notification isn't showing up?
Start with the OS-level settings. If Focus Assist (do not disturb) is enabled, notifications skip the banner and go straight to the notification center, and notifications can also be turned off per-app or system-wide in the Settings app. On the implementation side, the two common causes are that AppNotification isn't supported when sent from an elevated (administrator) process, and that plain Windows.UI.Notifications won't display anything when called from a desktop app without an AUMID.
Can a Windows service send toast notifications?
No, it can't. Since Windows Vista, services run in Session 0 and have no ability to present UI to interact with a user. If you need to notify the user, the standard pattern is to place a tray-resident agent process in the user's session and have it communicate with the service over IPC such as named pipes, with the agent itself responsible for displaying the notification.

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog