Invoke NotifyIcon's Context Menu
Asked Answered
M

4

43

I want to have it such that left clicking on the NotifyIcon also causes the context menu (set with the ContextMenuStrip property) to open as well. How would I achieve this? Do I have to handle Click and figure out the positioning myself?
Edit: showing the menu with trayIcon.ContextMenuStrip.Show() results is a few undesirable behaviors:

The menu is not shown at the same location as if right click the NotifyIcon (it appears that you can't set the x and y coords to where the taskbar is, at least on Windows 7 which is what I'm running). It will appear above the task bar (not that big of a deal, but consistency would be nice).

While the menu is shown, there is an extra icon added to the task bar.

Clicking somewhere other than the menu does not close it (whereas if you right click to bring up the context menu clicking else where automatically closes the context menu).

Is it at all possible to just invoke the menu however the built in right click handler is doing it?

Madison answered 5/2, 2010 at 16:37 Comment(0)
H
94

You would normally handle the MouseClick event to detect the click and call the ContextMenuStrip.Show() method:

    private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) {
        contextMenuStrip1.Show(Control.MousePosition);
    }

But that doesn't actually work properly, the CMS won't close when you click outside of it. Underlying issue is a Windows quirk (aka "bug") that is described in this KB article.

Invoking this workaround in your own code is pretty painful, the pinvoke is unpleasant. The NotifyIcon class has this workaround in its ShowContextMenu() method, they just made it difficult to get to since it is a private method. Reflection can bypass that restriction. I discovered this hack 5 years ago and nobody reported a problem with it yet. Set the NFI's ContextMenuStrip property and implement the MouseUp event like this:

using System.Reflection;
...
    private void notifyIcon1_MouseUp(object sender, MouseEventArgs e) {
      if (e.Button == MouseButtons.Left) {
        MethodInfo mi = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic);
        mi.Invoke(notifyIcon1, null);
      }
    }
Hardcore answered 5/2, 2010 at 17:7 Comment(6)
@msorens - not to late to edit :) Got some more tricks to solve the ApplicationContext workaround. Search my answers for SetVisibleCore.Hardcore
This solution is not using the possibility to assign the NotifyIcon menu property to the ContextMenuStrip object; and therefore, it is doing an overly ugly hack to solve a problem that shouldn't occur in the first place. Please see #133112 for further information.Lailaibach
That's complete nonsense.Hardcore
IlSpy illuminates this process slightly: a call to ShowContextMenu() is made after a WM_RBUTTONUP Win message to the NotifyIcon. ContextMenuStrip.Show() is indeed then called (from an internal method in ContextMenuStrip called ShowInTaskbar()). 3 things happen first: positioning arithmetic, [User32.dll].SetForegroundWindow() is called with the NotifyIcon HWnd as the parameter, and the internal property base.WorkingAreaConstrained is set to false in the ContextMenuStrip. One or both of these are presumably the magic sauce for the differing behaviour.Regazzi
@BobSammers With the reference source you can walk through the process step by step.Shelburne
How can I pass contextmenu x, y coordinates to showcontextmenu method through invoke? I want to position the context menu in a concret x,y coordinates. Thanks!Quinone
M
2

You can wire in a onClick event for notify icon then call show in the on click

private void wire()
{
     notifyIcon1.Click += new EventHandler(notifyIcon1_Click);
}

void notifyIcon1_Click(object sender, EventArgs e)
 {
    contextMenuStrip1.Show(Cursor.Position);
 }
Mixie answered 5/2, 2010 at 17:2 Comment(0)
C
2

If you handle MouseUp rather than Click, you will be able to tell which button was clicked, as well as the location of the click. You can use this location as the location to show the ContextMenu

notifyIcon.MouseUp += new MouseEventHandler(delegate(object sender, MouseEventArgs e) { contextMenu.Show(e.Location); });
Cooke answered 5/2, 2010 at 17:3 Comment(0)
M
2

use the following code to show context menu on both right and left click on notifyicon, if you find any issue then text me at [email protected] (arshad from Islamabd)
//System.Runtime.InteropServices use thi as reference

    [DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
    public static extern bool SetForegroundWindow(HandleRef hWnd);

    private void notifyIcon1_Click(object sender, EventArgs e)
    {
        SetForegroundWindow(new HandleRef(this, this.Handle));
        int x = Control.MousePosition.X;
        int y = Control.MousePosition.Y;
         x = x - 10;
        y = y - 40;
        this.contextMenuStrip1.Show(x,y );
        //this.PointToClient(Cursor.Position)
    }
Maybe answered 30/10, 2014 at 5:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.