WPF NotifyIcon
Version 1.0.8 released April 2nd 2016.
This is an implementation of a NotifyIcon (aka system tray icon or taskbar icon) for the WPF platform. It does not just rely on the Windows Forms NotifyIcon component, but is a purely independent control which leverages several features of the WPF framework in order to display rich ToolTips, Popups, context menus, and balloon messages. It can be used directly in code or embedded in any XAML file.
Browse/fork/clone on GitHub (includes sample application)
Download library via NuGet
Features at a glance
- Custom Popups (interactive controls) on mouse clicks.
- Customized ToolTips (Vista and above) with fallback mechanism for xp/2003.
- Rich event model including attached events to trigger animations in Popups, ToolTips, and balloon messages. I just love that.
- Full support for standard Windows balloons, including custom icons.
- Custom balloons that pop up in the tray area. Go wild with styles and animations 🙂
- Support for WPF context menus.
- You can define whether to show Popups on left-, right-, double-clicks etc. The same goes for context menus.
- Simple data binding for Popups, ToolTips and custom balloons through attached properties and derived data context.
- Command support for single / double clicks on the tray icon.
Tutorial and Support
-
-
- A comprehensive tutorial that complements the attached sample application can be found on the Code Project:
-
http://www.codeproject.com/KB/WPF/wpf_notifyicon.aspx
Please post support questions to the CodeProject forum only. Thank you.
Screenshots
The screenshots below were taken from NetDrives and the sample application.
XAML Declaration Sample
The sample below shows some of the properties of the control. For a more comprehensive sample, have a look at the sample application that comes with the download.
<Window x:Class="Hardcodet.NetDrives.UI.SystemTray.Sample" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:tb="http://www.hardcodet.net/taskbar"> <tb:TaskbarIcon x:Name="myNotifyIcon" Visibility="Visible" ToolTipText="Fallback ToolTip for Windows xp" IconSource="/Images/TrayIcons/Logo.ico" ContextMenu="{StaticResource TrayMenu}" MenuActivation="LeftOrRightClick" TrayPopup="{StaticResoure TrayStatusPopup}" PopupActivation="DoubleClick" TrayToolTip="{StaticResource TrayToolTip}" /> </Window>
@Sys
The problem seems to be that when shutting down the app with
Application.Current.Dispatcher.InvokeShutdown(); or similar
The GC finalizes the TaskbarIcon and Dispose(true) is never called and in turn RemoveTaskbarIcon().
If you change the Dispose(bool disposing) method as follows it will clean up the icon.
private void Dispose(bool disposing)
{
//don’t do anything if the component is already disposed
if (IsDisposed || !disposing)
{
// Cleanup unmanaged resources
RemoveTaskbarIcon();
return;
}
It’s great, but i’ve got some problems with the context menu of the icon.
If the menu has more than one item, the menu appears above the taskbar…
I don’t know what to do…in the sample everything works well, but even when I copy the same code into my project, there’s the bug with the wrong placement. Any ideas?
Alex
Hello
this is a nice work but i have a problem with the implementation. i try to use a context menu on your icon.
So the doublclick command is worked but the menue item doesn’t work. So have you any idea of this problem?
@Daniel
Hello
this is a nice work but i have a problem with the implementation. i try to use a context menu on your icon.
<!–
–>
So the doublclick command is worked but the menue item doesn’t work. So have you any idea of this problem?
very nice sir thnx a lot
WOW!!!!!!!!!!
this is fantastic!!!!!!
Hey, I just found this. This is a great help to what we are trying to accomplish. A quick question: Could you point me to where you pick up your knowledge of how to create the wrapper for Commands as you have them set up? I am looking to (potentially) expand upon them, and was wondering if expanding is really necessary. Anyway, thanks for the WPF NotifyIcon Set
Fantastic project, for newbies this kind of thing is great. Is there any chance you have a vb.net version laying around?
Hi, thanks for this great piece of code!
I just noticed a problem when you try to display Chinese or other non-latin characters in balloons: they’re displayed as ‘?’. Setting CharSet = Auto on NotifyIconData and Shell_NotifyIcon fixes the problem on Windows 7, but then it doesn’t work at all on Windows XP (neither balloons nor tooltips are shown)… any idea?
Hi, I’m having a problem using your fantastica lib.
When I create a fresh project (WPF .net 4.5) and configure you lib, everything works well.
The xaml that I’m using is
Then I change the .net version of my project to .net 4 (I need it…) and I start to get an exception that are throwed by CreateMessageWindow() on WindowsMessageSink. E exception happens because MessageWindowHandle is equals to IntPtr.Zero.
What me I doing wrong?
How can you have keyboard shortcuts on the NotifyIconWPF ContextMenu (e.g. press x after right-click, instead of clicking exit)?
I have made many failed attempts to use keyboard shortcuts on the context menu of NotifyIcon WPF. For example:
var exit_Item = new MenuItem();
exit_Item.Header = “E_xit”;
exit_Item.Click += new RoutedEventHandler(new System.EventHandler(ExitProgram));
myNotifyIconWpf.ContextMenu.Items.Add(exit_Item);
This does not work. The x shows up underlined, but pressing the key does nothing. I have tried explicitly binding to the PreviewKeyDown events of the NotifyIcon and the ContextMenu, etc., but nothing seems to work.
Thanks in advance.
This library isn’t compatible with AMD64 machines. Here’s a patch to fix the problem:
# HG changeset patch
# User Mark Junker
# Date 1358181753 -3600
# Node ID 74e2a24ae6e55a69c2a517d8290156766038f989
# Parent b5b8a1f4fb8ecef671834f2d8599c65263ccd7ab
Make API definition compatible for AMD64 builds/machines
diff -r b5b8a1f4fb8e -r 74e2a24ae6e5 NotifyIconWpf/Interop/WinApi.cs
— a/NotifyIconWpf/Interop/WinApi.cs Mon Jan 14 17:38:40 2013 +0100
+++ b/NotifyIconWpf/Interop/WinApi.cs Mon Jan 14 17:42:33 2013 +0100
@@ -21,15 +21,15 @@
[DllImport(“USER32.DLL”, EntryPoint = “CreateWindowExW”, SetLastError = true)]
public static extern IntPtr CreateWindowEx(int dwExStyle, [MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
[MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, int dwStyle, int x, int y,
– int nWidth, int nHeight, uint hWndParent, int hMenu, int hInstance,
– int lpParam);
+ int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance,
+ IntPtr lpParam);
///
/// Processes a default windows procedure.
///
[DllImport(“USER32.DLL”)]
– public static extern long DefWindowProc(IntPtr hWnd, uint msg, uint wparam, uint lparam);
+ public static extern long DefWindowProc(IntPtr hWnd, uint msg, IntPtr wparam, IntPtr lparam);
///
/// Registers the helper window class.
diff -r b5b8a1f4fb8e -r 74e2a24ae6e5 NotifyIconWpf/Interop/WindowClass.cs
— a/NotifyIconWpf/Interop/WindowClass.cs Mon Jan 14 17:38:40 2013 +0100
+++ b/NotifyIconWpf/Interop/WindowClass.cs Mon Jan 14 17:42:33 2013 +0100
@@ -7,7 +7,7 @@
/// Callback delegate which is used by the Windows API to
/// submit window messages.
///
– public delegate long WindowProcedureHandler(IntPtr hwnd, uint uMsg, uint wparam, uint lparam);
+ public delegate long WindowProcedureHandler(IntPtr hwnd, uint uMsg, IntPtr wparam, IntPtr lparam);
///
diff -r b5b8a1f4fb8e -r 74e2a24ae6e5 NotifyIconWpf/Interop/WindowMessageSink.cs
— a/NotifyIconWpf/Interop/WindowMessageSink.cs Mon Jan 14 17:38:40 2013 +0100
+++ b/NotifyIconWpf/Interop/WindowMessageSink.cs Mon Jan 14 17:42:33 2013 +0100
@@ -186,7 +186,7 @@
taskbarRestartMessageId = WinApi.RegisterWindowMessage(“TaskbarCreated”);
// Create the message window
– MessageWindowHandle = WinApi.CreateWindowEx(0, WindowId, “”, 0, 0, 0, 1, 1, 0, 0, 0, 0);
+ MessageWindowHandle = WinApi.CreateWindowEx(0, WindowId, “”, 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (MessageWindowHandle == IntPtr.Zero)
{
@@ -202,7 +202,7 @@
///
/// Callback method that receives messages from the taskbar area.
///
– private long OnWindowMessageReceived(IntPtr hwnd, uint messageId, uint wparam, uint lparam)
+ private long OnWindowMessageReceived(IntPtr hwnd, uint messageId, IntPtr wparam, IntPtr lparam)
{
if (messageId == taskbarRestartMessageId)
{
@@ -226,11 +226,11 @@
/// or higher, this parameter can be used to resolve mouse coordinates.
/// Currently not in use.
/// Provides information about the event.
– private void ProcessWindowMessage(uint msg, uint wParam, uint lParam)
+ private void ProcessWindowMessage(uint msg, IntPtr wParam, IntPtr lParam)
{
if (msg != CallbackMessageId) return;
– switch (lParam)
+ switch (lParam.ToInt32())
{
case 0x200:
MouseEventReceived(MouseEvent.MouseMove);
Is it possible to use this without a window?
Whether to wait for the 64-bit version?
Hi,
I have the right version 1.0.4 but I still cannot put text into the simple textbox included in popin.
What am I doing wrong ?
Thanks a lot,
@Philipp Sumi
This is a simplistic question, but i’m going to ask it anyway. I get the definition of the taskbaricon in the window example, but what happens you don’t have either a TrayMenu, TrayStatusPopup or a TrayToolTip in a resource somewhere?
@Jordan Morris
How can you have keyboard shortcuts on the NotifyIconWPF ContextMenu
Hi,
Firstly, I would like to thank and congratulate the NotifyIcon.
I have a WPF application with an inherited form of another class. This other class is not a form, but inherits from the Window class.
Could use your component to display the icon on tray. But to display the popup window, I am getting an exception.
This exception is occurring in this line “popup.Child = balloon;” (line 200), in a class TaskbarIcon. Can you help me with this?
Great product.
Feature request; can I specify the timespan for which to close the Balloon? It appears to be fixed at 60s.
Cheers!
I’m trying to use the TaskbarIcon in a console application which starts only as this icon. I tried creating the object in C# code, by creating a new class with the STAThread-attribute, and instantiating the TaskbarIcon in there. Like so:
// create the system tray icon
TaskbarIcon sysTrayIcon = new TaskbarIcon();
sysTrayIcon.Icon = myIcon;
sysTrayIcon.ToolTipText = “Tooltip”;
ContextMenu menu = new ContextMenu();
MenuItem item = new MenuItem { Header = “Close” };
item.Click += (s, e) => { Debug.WriteLine(“Clicked!”); };
menu.Items.Add(item);
sysTrayIcon.ContextMenu = menu;
Neither does the tooltip show up, nor does the context menu. If I click around on it, the tooltip or the context menu will appear randomly, but will not disappear and not react. It disappears when I close the application.
So I looked into your sample project, how you’re creating the TaskbarIcon in code. The only place where you actually do that is in the Forms sample. I did it exactly like you did in there, and it still didn’t work. However, when executing the same code in a Windows Forms application, for example in the constructor of the main window, it works as it should.
Do you (or anyone else) know why this is happening, and how I can fix it?
Current implementation as follows,
private void Dispose(bool disposing)
{
//don’t do anything if the component is already disposed
if (IsDisposed || !disposing) return;
IsDisposed = true;
WinApi.DestroyWindow(MessageWindowHandle);
messageHandler = null;
}
Since the window handle is unmanaged resource, it cannot be reclaimed by garbage collector automatically. Assume that you have forgotten invoking public Displose() method, GC will invoke Dispose(false). As a result the hidden window will still be alive. It is because “if (IsDisposed || !disposing) return;” has returned ahead of time. The correct implementation is as below:
private void Dispose(bool disposing)
{
//You can lock the following codes if thread safety is necessary.
if (!IsDisposed)
{
if (disposing)
{
// Release all the unmanaged resources namely
// all the members that are intances of managed classes implementing IDisposable interface)
// For this classs, there are no such members. Therefore do nothing.
}
WinApi.DestroyWindow(MessageWindowHandle);
messageHandler = null;
IsDisposed = true;
}
}
There seem to be an issue in method Dispose of WindowMessageSink. Current implementation as follows,
private void Dispose(bool disposing)
{
//don’t do anything if the component is already disposed
if (IsDisposed || !disposing) return;
IsDisposed = true;
WinApi.DestroyWindow(MessageWindowHandle);
messageHandler = null;
}
Since the window handle is unmanaged resource, it cannot be reclaimed by garbage collector automatically. Assume that you have forgotten invoking public Displose() method, GC will invoke Dispose(false). As a result the hidden window will still be alive. It is because “if (IsDisposed || !disposing) return;” has returned ahead of time. The correct implementation is as below:
private void Dispose(bool disposing)
{
//You can lock the following codes if thread safety is necessary.
if (!IsDisposed)
{
if (disposing)
{
// Release all the unmanaged resources namely
// all the members that are intances of managed classes implementing IDisposable interface)
// For this classs, there are no such members. Therefore do nothing.
}
WinApi.DestroyWindow(MessageWindowHandle);
messageHandler = null;
IsDisposed = true;
}
}
There seem to be an issue in method Dispose of WindowMessageSink. Current implementation as follows,
private void Dispose(bool disposing)
{
//don’t do anything if the component is already disposed
if (IsDisposed || !disposing) return;
IsDisposed = true;
WinApi.DestroyWindow(MessageWindowHandle);
messageHandler = null;
}
Since the window handle is a unmanaged resource, it cannot be reclaimed by garbage collector automatically. Assume that you have forgotten to invoke public Displose() method, GC will invoke Dispose(false). As a result the hidden window will still be alive. It is because “if (IsDisposed || !disposing) return;” has returned ahead of time. The correct implementation is as below:
private void Dispose(bool disposing)
{
//You can lock the following codes if thread safety is necessary.
if (!IsDisposed)
{
if (disposing)
{
// Release all the unmanaged resources namely
// all the members that are intances of managed classes implementing IDisposable interface)
// For this classs, there are no such members. Therefore do nothing.
}
WinApi.DestroyWindow(MessageWindowHandle);
messageHandler = null;
IsDisposed = true;
}
}
typo for@dj.y.noter . “Assume that you have forgotten invoking public Displose() method,” should be “Assume that you have forgotten to invoke public Displose() method, ”
Sorry for repeated comments
Upon targeting .Net 4 / 4.5, the sample app would generate an exception as the call to CreateWindowEx on line 190 of the WindowMessageSink.cs.
Commenting out the null ptr check would allow the code to function, and the icon to display, but it would disappear on mouse over or other interaction.
I’ve determined the cause was rooted in manually creating a window, the WndProc function must respond to the WM_NCCREATE message by returning TRUE. If it does not, the creation process will fail. In the current code, the message is passed to DefWindowProc which ought to return a value of 1, but does not.
The corrective fix I’ve made, that works for all .NET version 3.5 to 4.5 is to check to determine if the messageId passed to OnWindowMessageReceived is equal to 0x0081 (129 dec), and if so, return a 1 after calling DefWindow Proc as follows:
in WindowMessageSink.cs I’ve added
public const int WmNCCreate = 0x0081;
and modified OnWindowMessageReceived to read:
private long OnWindowMessageReceived(IntPtr hwnd, uint messageId, uint wparam, uint lparam)
{
if (messageId == taskbarRestartMessageId)
{
//recreate the icon if the taskbar was restarted (e.g. due to Win Explorer shutdown)
TaskbarCreated();
}
//forward message
ProcessWindowMessage(messageId, wparam, lparam);
// Pass the message to the default window procedure
long result = WinApi.DefWindowProc(hwnd, messageId, wparam, lparam);
result = messageId == WmNCCreate ? 1 : result;
return result;
}
The call to CreateWindowEx now receives a valid ptr.
Can you please update the nuGet package after making a change to the assemblyinfo.cs
[assembly: CLSCompliant(true)]
My company policy is that all assemblies are CLSCompliant and this would make it cleaner for me rather than to download source and do it myself.
What about maybe adding a strong name to the nuget package too?
I have a strong named application and I need to use reflection to load your DLL as strong named applications must only reference strong named libraries.
Thanks
@P.Vilevac
Thanks that fixed my problem 😀
I would also be very interested in knowing whether this component is going to be available for .NET 4+ and x64 platforms. Having to compile some applications in x86 just for this component makes me feel kind of old fashioned… Anyone has clues or know anything about the future of this component?
I am using your solution in an MVVM app, working great. I would like the trayicon to pop up a balloon without the user having to click on or hover over it. I can’t see a property that exposes show pop up balloon. Am i doing it wrong, is there a better way to do this, I am using MVVM so tb.ShowBalloonTip doesn’t really work.
@P.Vilevac That fixes the issue at startup but while bringing up the context menu the app still crashes. Observing this behavious on x64 machines and Windows 8
@P.Vilevac
Hello
I tried your suggested change after NotifyIcon stopped working after using .NET 4.5. However, when I right-click on the taskbar icon, the application crashes with the message:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
HResult is -2147467261.
Any idea what might be causing this?
Thank you in advance.
@P.Vilevac
Sorry. Forgot to say that the app crashed at the following line:
long result = WinApi.DefWindowProc(hwnd, messageId, wparam, lparam);
Hope that helps.
@prat
I’m loading VS 2013 to see if that helps identify the problem better than 2012.
I am also running x64 Windows 8.
I’ll let you know how that goes.
@P.Vilevac
I loaded VS 2013 and it didn’t really help. I think the issue is that WinApi.DefWindowProc is called many times and somewhere on the way, when the notify icon is right-clicked, memory gets corrupted but I can’t see what exactly is corrupted (debug has apparently valid values in the standard patrameters). I am not a C++ developer and don’t know where to go from here. The only thing I can see that might help is that an extra message says:
A first chance exception of type ‘System.AccessViolationException’ occurred in TaskbarNotification.dll
followed by
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I would really appreciated any help that anyone can offer, because I fear I will have to abandon using NotifyIcon for .NET 4.5 forward, and I really love it.
@Mark Junker
Woopee! Thanks a million Mark.
The rest of you may have missed it, because Mark’s contribution was marked (sorry, pun not intended) as a fix for AMD machines. It also has the benefit of fixing the .NET 4.5 and 4.5.1 issue. I suspected the issue was to do with data types for CreaWindowEx() parameters, because all of the C++ examples I have seen use different data types as well. So, I thought I would give it a go, and it worked.
No need for all of the workarounds – the original code works but for Mark’s changes. A tribute to Philipp’s original work.
Well done Mark. I don’t know how you figured it out, but good on you.
Cheers, from a grateful coder – I was on the point of giving up.
Tony
@Mark Junker @Tony Vaughan
A really big thank you for your patch, and to philipp for their work.
It would be great to include the patch in a new release version.
//Note: XAML is suggested for all but the simplest scenarios
TaskbarIcon tbi = new TaskbarIcon();
tbi.Icon = Resources.Error;
tbi.ToolTipText = “hello world”;
>> Error: The type or namespace name ‘TaskbarIcon’ could not be found (are you missing a using directive or an assembly reference?)
How do i import the lib?
Great Project, have really helped me out.
But still have a major problem,
Why does PreviewKeyDown, KeyDown or TextChanged never fire??
Need it on a textbox in the ContextMenu for the TaskbarIcon.
Fixed in the new version. I’ll deploy that one soon. Please post back if this is currently a show-stopper for you.
Hi Philipp, amazing work.
Have you tried using an animated GIF instead of a PNG, because the animation is not displayed.
Thanks
Hi Diego
Could you elaborate please? There’s no animations in the Taskbar (those are icon (*.ico) files), and the animation in the sample is a WPF storyboard that just rotates the PNG image…
It would be great if CreateTaskbarIcon/RemoveTaskbarIcon could be accessed from ‘public’, to show/hide icon without recreating the whole resource
Hi, thank you for NotifyIcon. It’s a great library.
I’ve noticed that LeftClickCommand is invoked with a small delay after click on icon.
In the same time there is no such delay for DoubleClickCommand.
Although LeftClickCommand delay is small it is still noticable to users using the application.
It would be great to include a fix for this small issue in your future release.
Thanks
Sorry , I forgot to say
I need to change the IconSource in Codebehind, how do I write in cs code ?
Hi,
I have a problem when my application (which uses this library) is run in a Citrix environment where no access to the application bar is available.
The problem is that the call to GetSystemTaskBarPosition throws an exception (Interop/TrayInfo.cs, GetTrayLocation).
Here is our preliminary workaround:
var process = System.Diagnostics.Process.GetCurrentProcess();
var hwndMain = process.MainWindowHandle;
var screen = System.Windows.Forms.Screen.FromHandle(hwndMain);
if (screen == null)
throw new Exception(“Monitor for main window not found.”);
return new Point { X = screen.WorkingArea.Right, Y = screen.WorkingArea.Bottom };
As you can see, we just use the working area of the main monitor – which is our best guess…
BTW: We just used the functionality from System.Windows.Forms, but this might inconvenient for you.
Hi!
I’m trying to create an Click Once publish for my application but I’ve received an message that your assembly is not signed. Do you have an signed version? please?
Error 8 Assembly generation failed — Referenced assembly ‘Hardcodet.Wpf.TaskbarNotification’ does not have a strong name
Thank you.
Michel,
The current version isn’t signed – this will make adoption harder for the vast majority of people who use it. I’d recommend you just sign a version yourself.
Thanks, Philipp
It would be good to put the source for this up on GitHub for easy browsing?
They’ll be online soon.