Dual Menus: Add a Left-click Menu to your Application's Tray Icon






4.68/5 (14 votes)
This article will show you a trick to spoof a right-click on your tray icon to support dual contextual menus for your application's NotifyIcon.

NotifyIcon
context menu.Introduction
Special thanks to Rick Valstar for the original idea and solution to this problem. Download Rick's original solution in VB.NET above.
This article will show you how to relatively painlessly add a left-click context menu to your program's system tray icon, complete with a double-click timer to allow for double-clicks.
Background
A short while ago I decided it would be neat to equip my application's tray icon with an alternate menu interface that could be activated by a left mouse click, in addition to its existing right-click menu. However, I soon discovered this was easier said than done.
Although at first it may not sound like a very difficult problem if you've never attempted it, adding a left-click menu to a tray icon can be a nightmare if you're not prepared. In fact, the naive (read: intuitive) approach has so many problems with it you might just give it up for a lost cause. But don't give up just yet, there is a solution, but first let's look at exactly why this is such a tricky problem.
The Problem
My first approach was to simply create a ContextMenuStrip
, populating it with the appropriate menu items, and Show()
it at the cursor's current position, like I've done many times before in other situations. However, as the following image demonstrates, there were a number of problems with this approach:

- A strange ghost form appears in the task bar whenever the context menu is visible. Note that we haven't created any additional form objects at all here! We've only called
ContextMenuStrip.Show()
. - The menu itself stays above the task bar, which results in a different appearance than that of the normal context menu, which appears directly above the cursor, slightly overlapping the top of the task bar (see Figure 1).
- To top it all off, the menu doesn't close when you click away from it! I've illustrated this by clicking the Outlook icon. Notice how our context menu is still visible even though it is in the background. The menu's
AutoClose
property also has no effect here.
Suffice it to say that manually showing a ContextMenuStrip
is not going to solve our problem. Instead, we need to be a little more clever.
Let's examine the evidence: we know that the NotifyIcon
does support context menus, it just doesn't seem to like anything other than a right-click to activate them. Furthermore, we can set the context menu via the NotifyIcon
's ContextMenuStrip
property, meaning the menu isn't hard-wired to the tray icon.
The Solution
So what do we do? Well, if the NotifyIcon
only likes right-clicks, why upset it? We'll give it a right-click, but trick it into performing on our terms. We do this by listening for left-clicks on the icon, and spoofing a right-click whenever we receive one, after, of course, we swap out the default menu for our alternate one.
So, let's get to business.
Using the Code
The first thing we'll want to do is attach four components to our main form:

- A
NotifyIcon
to represent our application in the system tray - A
ContextMenuStrip
for the left-click menu - A
ContextMenuStrip
for the right-click menu - A
Timer
to delay the left-click menu by the maximum time of a double-click
We'll want to set the timer's interval to match that of the system's double-click time:
public DemoForm() {
InitializeComponent();
click_timer.Interval = SystemInformation.DoubleClickTime;
}
We'll also want to initially set the NotifyIcon
's ContextMenuStrip
to our right-click menu, as that will be the context menu we want to be shown by default.
Next we'll wire up the NotifyIcon
's MouseClick
event to check for a left-click, and start the timer if we receive one:
private void notifyIcon_MouseClick( object sender, MouseEventArgs e ) {
if ( e.Button == MouseButtons.Left )
click_timer.Start();
}
The timer allows us to wait a fraction of a second to see if the user is going to click the mouse again, resulting in a double-click. If this occurs, we'll want to cancel the timer to avoid showing the left-click menu:
private void notifyIcon_DoubleClick( object sender, EventArgs e ) {
click_timer.Stop(); // cancel the left-click menu
this.RestoreState();
}
Now, the real magic happens in the click timer's Tick
event handler, where we construct and send out a fake right-click event at the cursor's current location. Be sure to stop the click timer, otherwise we'll keep getting spoofed right-clicks that will never stop!
private void click_timer_Tick( object sender, EventArgs e ) {
click_timer.Stop(); // make sure it doesn't keep firing!
// here's where the magic happens
// first, switch the icon's contextmenu to the left-click menu
notifyIcon.ContextMenuStrip = left_menuStrip;
// construct a right-click spoof input
INPUT[] rightClick = new INPUT[2];
MOUSEINPUT rightDown = new MOUSEINPUT();
rightDown.dwFlags = Win32.MOUSEEVENTF_RIGHTDOWN + Win32.MOUSEEVENTF_ABSOLUTE;
rightDown.dx = 0; // 0,0 means current cursor position
rightDown.dy = 0;
rightDown.time = 0;
rightDown.mouseData = 0;
MOUSEINPUT rightUp = new MOUSEINPUT();
rightUp.dwFlags = Win32.MOUSEEVENTF_RIGHTUP + Win32.MOUSEEVENTF_ABSOLUTE;
rightUp.dx = 0;
rightUp.dy = 0;
rightUp.time = 0;
rightUp.mouseData = 0;
rightClick[0] = new INPUT();
rightClick[0].type = Win32.INPUT_MOUSE;
rightClick[0].mi = rightDown;
rightClick[1] = new INPUT();
rightClick[1].type = Win32.INPUT_MOUSE;
rightClick[1].mi = rightUp;
// finally, send the spoofed right-click to invoke the menu
Win32.SendInput( 2, rightClick, Marshal.SizeOf( rightClick[0] ) );
}
... and finally, we have to be sure to reset the NotifyIcon
's ContextMenuStrip
property to its default right-click menu, when the user is finished with it:
private void left_menuStrip_Closed( object sender, ToolStripDropDownClosedEventArgs e ) {
// reset the menu to the default (right-click) menu
notifyIcon.ContextMenuStrip = right_menuStrip;
}
Now when we build and run, here are the results of a left-click:

SendInput()
The key to all of this is the Win32 SendInput()
function, which will allow us to spoof a right-click without actually requiring the user to ever press the right mouse button. Because this is a Win32 function, we'll have to access it via Interop services, hence this article is deemed Intermediate and not Beginner.
Thanks to PInvoke.net, we've equipped our demo project with all of the declarations needed to invoke SendInput()
. Please download the complete source code for all of the definitions as there are too many to list here.
Here is the signature for the SendInput()
function:
[DllImport( "user32.dll", SetLastError = true )]
public static extern uint SendInput( uint nInputs, INPUT[] pInputs, int cbSize );
For details on the SendInput
function, check out Microsoft's documentation on MSDN.
Points of Interest
The SendInput
function is quite a powerful Win32 function, and this is only one example of how it can come in handy, but it can also be quite dangerous. Please be sure to read the official documentation if you intend to use it yourself to solve a different problem. Remember, spoofed inputs affect all programs, not just the application that creates them.
That said, if you've used SendInput
to solve a similar problem, please post a comment and let us know what you used it for.
I hope you found the article useful. Please use the comments section to discuss any points raised, or to ask about anything that wasn't quite clear.
History
- Thursday, Sep 27, 2007: First publication