System Tray Icons and Toast Notifications in Windows Apps — NotifyIcon Pitfalls and Choosing the Right AppNotification API
· Go Komura · 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 setIconand setVisible = true(the default isfalse). The tooltipTextcan 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
UseWPFandUseWindowsFormsin 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 = trueinFormClosingand callingHide(). Always provide a separate, explicit way to exit (an “Exit” item on the tray menu), and checkCloseReasonso 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.Notificationsis “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
IconandVisible = trueare set. The default value ofVisibleisfalse.1 Texthas 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 anArgumentException. 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 = false → Dispose(). 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.
ShowBalloonTipis 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.Notificationsfrom 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
NotificationInvokedbefore callingRegister(). 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, alongsideNotifyIcon.Dispose()(there’s also a separateUnregisterAll()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.
AppNotificationManagerdepends 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.
Showdoesn’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)
- The service focuses purely on monitoring and business logic, and sends anything it needs to notify about to the agent over IPC.
- The agent is a tray-resident app (exactly what Chapters 2–8 of this article describe) that launches when the user logs on, and it displays whatever events it receives as toasts or icon changes.
- We covered how to choose an IPC mechanism in “How to choose Windows inter-process communication,” how to build the service side in “How to build and operate a Windows service,” and the session mechanism itself in “How to understand Windows session isolation — Session 0, RDP, and running multiple users at once.”
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 viaIcon+Visible = true, keepTextwithin the 127-character limit (.NET 6+), and make sureDispose()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 forCloseReason.UserClosingand 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
AppNotificationManageris the only real choice. It works even from an unpackaged exe with no package identity, andRegister()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 aTag/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.
Related Articles
- Preventing multiple launches of a Windows app — named mutexes and activating the running instance on a second launch
- How to build and operate a Windows service — from choosing between it and Task Scheduler to turning a BackgroundService into a service
- How to choose Windows inter-process communication — named pipes / TCP / gRPC / shared memory / COM decision table
- How to understand Windows session isolation — Session 0, RDP, and running multiple users at once
- Why use .NET Generic Host and BackgroundService in a desktop app
- A concrete way to isolate only the parts of a Windows app that require administrator privileges
- Best practices for checking and displaying external device status — designing beyond just “Connected”
Related Consulting Areas
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
-
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
-
Microsoft Learn, NotifyIcon.Text Property. On the limit for the tooltip text and the ArgumentException thrown when it’s exceeded. ↩ ↩2
-
Microsoft Learn, Windows Forms Controls and Equivalent WPF Controls. On WPF having no control equivalent to NotifyIcon. ↩ ↩2
-
Microsoft Learn, MSBuild properties for .NET desktop projects. On being able to enable both UseWPF and UseWindowsForms together. ↩ ↩2
-
Microsoft Learn, Form.FormClosing Event. On the event firing before the form closes, and Cancel letting you cancel the close. ↩ ↩2
-
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
-
Microsoft Learn, Windows Community Toolkit — Notifications (archive). On the Notifications component being archived and replaced by Windows App SDK app notifications. ↩
-
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
-
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
-
Microsoft Learn, App notifications overview. On sending notifications from an elevated app being unsupported. ↩ ↩2
-
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
-
Microsoft Learn, Focus assist. On focus assist suppressing the notification banner and routing it directly to the notification center. ↩ ↩2
-
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
-
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. ↩
-
Microsoft Learn, FormClosingEventArgs Class. On CloseReason letting you determine why the form is closing. ↩
-
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. ↩
-
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
-
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. ↩
-
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. ↩
-
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. ↩
-
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. ↩
-
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. ↩
-
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
-
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. ↩
-
Microsoft Learn, Remove app notifications. On attaching tags/groups, removing already-shown notifications, and setting expiration. ↩
-
Microsoft Learn, App notification content. On scenario=”urgent” notifications taking priority over Do Not Disturb settings with user consent. ↩
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Localizing WinForms/WPF Apps — resx, Satellite Assemblies, and Culture Switching in Practice
A practical guide to localizing Windows desktop apps, covering the difference between CurrentCulture and CurrentUICulture, how resx and s...
Printing and PDF Output in Windows Business Apps — Choosing Between System.Drawing.Printing, WPF, and Report Libraries
Organizes WinForms printing with PrintDocument, WPF FlowDocument/FixedDocument printing, and PDF output options into a requirement-based ...
Integrating Entra ID Authentication into WinForms/WPF Apps — A Practical Architecture with MSAL.NET and the WAM Broker
A practical, hands-on look at integrating Entra ID (formerly Azure AD) authentication into WinForms/WPF desktop apps: the public client m...
Windows App Outsourcing and Contract Development: What to Sort Out Before You Ask
Before commissioning Windows app outsourcing or contract development, here is how to sort out existing software modification, device inte...
Why Use the .NET Generic Host and BackgroundService in Desktop Apps
How to use the Generic Host and BackgroundService to organize startup, periodic processing, shutdown, logging, configuration, and DI in W...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
UI Threading & Timers
Topic page for WPF / WinForms UI threading, async flow, Dispatcher usage, and timer decisions.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
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.
Public links