调用NotifyIcon的上下文菜单

43

我希望左键单击通知图标也能打开与ContextMenuStrip属性设置的上下文菜单。如何实现这一点?我需要处理Click事件并自己确定位置吗?
编辑:使用trayIcon.ContextMenuStrip.Show()显示菜单会导致一些不良行为:

与右键单击通知图标时显示的位置不同(似乎无法将x和y坐标设置到任务栏所在的位置,至少在我运行的Windows 7中是这样)。它会出现在任务栏上方(这并不是很大的问题,但保持一致性会更好)。

当菜单显示时,任务栏会添加一个额外的图标。

单击菜单以外的地方不会关闭它(而右键单击以打开上下文菜单后,单击其他地方会自动关闭上下文菜单)。

是否有可能只是调用内置的右键处理程序来触发菜单?

4个回答

93

您通常会处理MouseClick事件以侦测点击并调用ContextMenuStrip.Show()方法:

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

但这实际上并不起作用,当您在外部单击时,CMS不会关闭。根本问题是Windows的一种怪癖(也称为“错误”),在此KB文章中有描述。

在您自己的代码中调用此解决方法非常痛苦,P/Invoke很不友好。NotifyIcon类在其ShowContextMenu()方法中使用此解决方法,但它们只是使其难以到达,因为它是一个私有方法。反射可以绕过该限制。我发现这个技巧已经有5年了,但至今没有人报告过任何问题。设置NFI的ContextMenuStrip属性,并像这样实现MouseUp事件:

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);
      }
    }

@msorens - 现在修改还不晚 :) 我有一些解决ApplicationContext的技巧。搜索我的答案,找到SetVisibleCore。 - Hans Passant
这个解决方案没有使用将NotifyIconmenu属性分配给ContextMenuStrip对象的可能性;因此,它正在进行一种过度丑陋的黑客攻击来解决本来不应该出现的问题。请参见https://dev59.com/fUXRa4cB1Zd3GeqPqk0t获取更多信息。 - 7heo.tk
5
那完全是胡说八道。 - Hans Passant
IlSpy 稍微解释了这个过程:在 WM_RBUTTONUP Win 消息发送到 NotifyIcon 后,会调用 ShowContextMenu()。然后确实会调用 ContextMenuStrip.Show()(从名为 ShowInTaskbar() 的内部方法中)。首先发生了三件事情:定位算术、[User32.dll].SetForegroundWindow() 以 NotifyIcon HWnd 作为参数被调用,并且在 ContextMenuStrip 中将内部属性 base.WorkingAreaConstrained 设置为 false。其中一个或两个可能是不同行为的魔法酱料。 - Bob Sammers
@BobSammers 通过参考源代码,您可以逐步了解整个过程步骤 - Scott Chamberlain
我该如何通过invoke方法将contextmenu的x、y坐标传递给showcontextmenu方法?我想将上下文菜单定位在具体的x、y坐标上。谢谢! - Willy

2

使用以下代码在通知图标上右键和左键单击以显示上下文菜单,如果您发现任何问题,请发送邮件至arshad_mcs786@hotmail.com(来自伊斯兰堡的Arshad)
//System.Runtime.InteropServices将其用作参考

    [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)
    }

2
你可以为通知图标连线一个 onClick 事件,然后在点击时调用 show 方法。
private void wire()
{
     notifyIcon1.Click += new EventHandler(notifyIcon1_Click);
}

void notifyIcon1_Click(object sender, EventArgs e)
 {
    contextMenuStrip1.Show(Cursor.Position);
 }

2
如果你处理MouseUp而不是Click,你将能够知道哪个按钮被点击,以及点击的位置。你可以使用此位置作为显示上下文菜单的位置。
notifyIcon.MouseUp += new MouseEventHandler(delegate(object sender, MouseEventArgs e) { contextMenu.Show(e.Location); });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接