Library / SDK
HavenDV/H.NotifyIcon avatar
HavenDV/H.NotifyIcon

H.NotifyIcon is a tray icon control that never touches Windows Forms

TrayIcon for WPF/WinUI/Uno/MAUI

771 stars59 forksC#MIT

At a glance

What is it?
H.NotifyIcon is a .NET 6 implementation of the Windows system tray icon for WPF, WinUI, Uno and MAUI that provides rich tooltips, popups, context menus and balloon messages as a native control rather than a wrapper around the Windows Forms NotifyIcon.
Who is it for?
H.NotifyIcon suits a Windows desktop application that needs a real system tray presence with tooltips, popups and a themed context menu, and it suits a team that wants the tray icon declared in XAML like any other control instead of wiring up interop by hand.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A control, not a wrapper around the Windows Forms icon

H.NotifyIcon is an implementation of a NotifyIcon, also known as a system tray icon or taskbar icon, for .Net 6 WPF, WinUI, Uno.Skia.WPF and Console platforms. The README makes one distinction clearly, and it is the reason the library exists: it does not just rely on the Windows Forms NotifyIcon component, but is a purely independent control which leverages several features of the WPF and WinUI frameworks in order to display rich tooltips, popups, context menus and balloon messages.

The point of not reusing Windows Forms is not ideological. A Windows Forms NotifyIcon is a WinForms control, so bringing one into a WPF application means hosting a WinForms element inside a WPF window, which drags in a second rendering stack, a second set of resources and its own theming behaviour. Writing the tray icon against the WPF and WinUI object model instead means the tooltip is a WPF popup, the context menu is a WPF or WinUI menu, and the theme follows the app.

The project is explicitly a continuation. The README describes it as a continuation of the inactive base project to other platforms, WinUI, Uno.Skia.Wpf and Console, and links the original hardcodet/wpf-notifyicon repository, which is where a WPF user would otherwise start.

The feature list is short and specific: notifications, context menus including native light and dark theme support, ICommand support, design-time access, efficiency mode, dynamic icon generation, and trimming and NativeAOT support.

Two of those are unusual enough to be worth attention. ICommand support means the tray menu can bind to commands rather than to event handlers, which is what makes the control usable in a data-bound application. Trimming and NativeAOT support matters for modern .NET applications that care about publish size and startup, since a control that pulls in reflection heavy infrastructure can block those settings.

Four packages, and a core library for console applications

The library ships as several NuGet packages, one per UI stack, and the choice is made at install time rather than at build time.

The README lists four installs:

powershell
Install-Package H.NotifyIcon.Wpf
Install-Package H.NotifyIcon.WinUI
Install-Package H.NotifyIcon.Uno
Install-Package H.NotifyIcon.Uno.WinUI

And then a fifth option, described in a comment in the same block as the way to reach other platforms, because it allows you to make a NotifyIcon even in a console application:

powershell
Install-Package H.NotifyIcon

The naming tells you the mapping. H.NotifyIcon.Wpf targets WPF, H.NotifyIcon.WinUI targets WinUI, H.NotifyIcon.Uno targets the Uno platform, and H.NotifyIcon.Uno.WinUI targets Uno applications that use WinUI rather than the Uno native stack. The bare H.NotifyIcon package is the shared core.

The core package is the interesting one for architecture, because it means the tray logic is not tied to a UI framework at all. A console application that needs a tray icon, for instance an agent or a background tool that a user should be able to reach, can take the core library and skip the WPF and WinUI packages entirely. That is the practical reason the project was continued beyond WPF.

The repository mirrors the package structure. The tree carries H.NotifyIcon.slnx as the main solution, H.NotifyIcon.PR.slnx for pull request builds, and a solution filter for MAUI, H.NotifyIcon.Maui.slnf, which tells you a MAUI target exists in the development setup even though the README install list does not include a MAUI package. There is a global.json pinning the SDK, a settings.xamlstyler file for XAML formatting, an .editorconfig, and a src/ directory holding the projects.

The README also mentions the project's own supporting files at the top level, including AGENTS.md, CLAUDE.md, SUMMARY.md and LICENSE.md, which are the files to consult for contribution rules and for the licence terms.

The tray icon is declared in XAML, with the namespace swapped per platform

The usage example is a single element in a window, and the only thing that changes between platforms is the namespace declaration:

xml
<Window
    xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf" // WPF
    xmlns:tb="using:H.NotifyIcon" // WinUI
    >
    <tb:TaskbarIcon
        ToolTipText="ToolTip"
        IconSource="/Images/TrayIcons/Logo.ico"
        ContextMenu="{StaticResource TrayMenu}"
        MenuActivation="LeftOrRightClick"
        TrayPopup="{StaticResource TrayStatusPopup}"
        PopupActivation="DoubleClick"
        TrayToolTip="{StaticResource TrayToolTip}"
        />
</Window>

The WPF form uses the clr-namespace and assembly syntax, and the WinUI form uses the newer using syntax, which is the usual way a .NET type is referenced in WinUI XAML. Everything else in the element is the same on both.

Reading the attributes tells you the whole feature set of the control. ToolTipText is the plain text shown on hover, TrayToolTip is the richer tooltip you can fill in XAML, TrayPopup is a popup surface attached to the icon, ContextMenu is the menu shown on right click, and the two activation properties decide which gesture opens what. MenuActivation="LeftOrRightClick" and PopupActivation="DoubleClick" mean the menu opens on either click while the popup needs a double click, which is a deliberate way to keep the two surfaces from fighting over the same gesture.

The control can also be used directly in code rather than in XAML, which the README mentions, and that matters for applications that build their window in code or that need to create the icon before a window exists.

Efficiency mode exists because a tray app is a background app

Windows 11 introduces a new concept called Efficiency Mode, and the README links the Microsoft documentation for it. The reasoning the README gives for implementing an API for this mode is about the library's intended use: since, basically, this library is intended for applications to exist in the background with the ability to interact through TrayIcon, the library implements an API for this mode.

The API is a small set of entry points:

cs
EfficiencyModeUtilities.SetEfficiencyMode(bool value)
WindowExtensions.Hide(this Window window, enableEfficiencyMode: true) // default value
WindowExtensions.Show(this Window window, disableEfficiencyMode: true) // default value
TaskbarIcon.ForceCreate(bool enablesEfficiencyMode = true) // default value

The shape of it is instructive. There is a direct setter, and then the hide and show extension methods on a window take a flag with a sensible default, so hiding a window puts the process into efficiency mode and showing one takes it back out. ForceCreate on the icon does the same at the moment the icon is created, and it also shows the second reason this API exists.

ForceCreate is about icon creation timing. A tray icon is usually created lazily, and in a WPF application the icon often needs to exist before the first message pump is running. Creating it explicitly, with the efficiency mode flag applied at the same moment, means the process registers as a background process from the start rather than being corrected later.

The limitation to keep in mind is that this is a Windows 11 feature. The library targets .Net 6 across WPF, WinUI, Uno and Console, and on Windows 10 the effect of these calls is not something the README documents, so the code has to be written so that a tray app is still correct without the efficiency mode being honoured.

Generated icons draw the tray glyph out of text

The dynamic icon generation feature lets the tray glyph be produced from a character and some formatting rather than from an image file, which is a practical answer to the problem of an icon that has to change at runtime.

The control nests a GeneratedIconSource inside the IconSource property:

xml
<tb:TaskbarIcon
    IconSource="/Icons/Error.ico"
    >
    <tb:TaskbarIcon.IconSource>
        <tb:GeneratedIconSource
            Text="5"
            Foreground="Black"
            FontSize="36"
            FontWeight="Bold"
            />
    </tb:TaskbarIcon.IconSource>
</tb:TaskbarIcon>

So the glyph is a number rendered in a bold font, which is the shape of a badge count. The earlier examples in the README show the same element with an emoji character and a foreground colour, and one with a background colour and a font family, and one with a border thickness and gradient brushes for both the foreground and the border.

The set of properties is the point. Text, Foreground, FontSize, FontWeight, FontFamily, Background and BorderThickness are all settable, and the foreground and border can each hold a LinearGradientBrush rather than a flat colour, which is what the last example does.

This matters for a specific kind of application. A chat client with an unread count, a build tool with a queue depth, a monitoring agent with a status indicator, all of them need a tray icon that changes without shipping a set of icon files for every state. A generated glyph removes that work, and because the rendering happens in the same UI stack as the rest of the app, the result is a native looking small icon rather than a scaled bitmap.

The design-time access note is related. The README recommends pinning the designer icon for easy viewing, which is done in Taskbar Settings, Other system tray icons, and enabling the icon there. The reason is that a tray icon is easy to lose behind the overflow chevron, so during development you want it always visible.

WinUI context menus have three modes and one of them is a preview

The WinUI side has a specific problem to solve, and the README describes the three answers with their trade-offs.

The first mode, based on your MenuFlyout, creates a Win32 PopupMenu that calls the commands attached to your MenuFlyoutItem, and this is the default. The second mode creates the menu in your open window, in the corner of the screen. The third mode creates a second transparent window used to render the native menu, and at the moment it is in the preview stage, requiring you to explicitly set ContextMenuMode="SecondWindow".

That last part deserves emphasis. A WinUI application has no native Win32 context menu of its own, so a tray icon menu has to be faked somehow. PopupMenu mode reconstructs a native menu from a WinUI flyout, which gives a native look and native theming at the cost of translating between the two object models. The corner-of-screen mode simply draws the flyout in the application, which is the least native but the least surprising. The second transparent window renders the menu outside the application window, which solves the problem of the menu being clipped by the app, and is the one still marked as a preview.

Theming is handled by a separate property. Native PopupMenu mode follows the Windows app theme by default through ContextMenuThemeMode="System". You can force a specific native menu theme with "Light" or "Dark", or use "Default" to go with the Windows default native menu app mode, and the same property is respected by SecondWindow mode for its WinUI flyout theme.

So the choice a developer faces is not just visual. PopupMenu means your MenuFlyout commands are what run, and the menu is native looking. The in-window mode is a plain flyout. The second window mode is a preview that solves a real layout problem but should not be shipped as the only path yet.

Editorial conclusion

H.NotifyIcon suits a Windows desktop application that needs a real system tray presence with tooltips, popups and a themed context menu, and it suits a team that wants the tray icon declared in XAML like any other control instead of wiring up interop by hand. It is the wrong tool for a cross-platform application, since the tray icon it implements is a Windows concept, and it is the wrong tool if you need a stable release today, because the recent published versions are 2.5.0 prereleases and the WinUI SecondWindow context menu mode is still described as a preview. Before you adopt it, install the package that matches your UI framework, since there are four, and pin a specific version rather than tracking the beta feed.

Frequently asked questions

Which H.NotifyIcon package should I install?

It depends on your UI stack. The README lists H.NotifyIcon.Wpf for WPF, H.NotifyIcon.WinUI for WinUI, H.NotifyIcon.Uno for Uno and H.NotifyIcon.Uno.WinUI for Uno applications using WinUI, and the bare H.NotifyIcon package for other platforms such as console applications.

How do I add a tray icon to a WPF window?

Declare a tb:TaskbarIcon element inside the window, with the namespace declared as clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf for WPF or using:H.NotifyIcon for WinUI. You set ToolTipText, IconSource, ContextMenu, MenuActivation, TrayPopup, PopupActivation and TrayToolTip on that element.

What is efficiency mode in H.NotifyIcon and why does it matter for a tray application?

Efficiency Mode is a Windows 11 concept, and the library exposes an API for it because its intended use is applications that live in the background and are reached through the TrayIcon. The API includes EfficiencyModeUtilities.SetEfficiencyMode(bool value) plus hide and show extension methods on a window and TaskbarIcon.ForceCreate, all with efficiency mode enabled by default.

Can H.NotifyIcon draw a tray icon from text instead of an image file?

Yes. A GeneratedIconSource placed inside the TaskbarIcon.IconSource property renders the glyph from a Text value, with settable Foreground, FontSize, FontWeight, FontFamily, Background and BorderThickness, and the foreground and border can hold gradient brushes.

Is the H.NotifyIcon WinUI context menu finished?

Not all of it. Three modes are implemented, the default PopupMenu mode, a menu drawn in the corner of your open window, and a second transparent window mode, and the README describes the second window mode as being in the preview stage, requiring you to set ContextMenuMode="SecondWindow" explicitly.

Official sources

  1. HavenDV/H.NotifyIcon on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/havendv-h-notifyicon.svg)](https://hysenlabs.com/projects/havendv-h-notifyicon)

Was this useful?