您的位置:首页 > 其它

winform 托盘图标左击显示菜单定位和点击任意位置消失

2018-03-31 16:18 741 查看
想自己做一个wnmp的集成环境软件,第一步在制作小图标点击弹出菜单时遇到问题。
winform中的控件是可以添加contextmenustrip的,效果是右击展开,点击任意不是contextmenustrip的地方消失,我想做一个左击的菜单和一个右击的菜单,效果和contextmenustrip的表现一样。如图:


(右键)  

(左键)

面向百度编程过程中,找到几个解决方法。
一、反射
这个是我现在采用的方法,博客原地址, 通过反射,解决这个问题,以下是我自己在鼠标的mousedown事件中的代码,private void programEntry_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left) //单击鼠标左键也弹出菜单
{
    this.programEntry.ContextMenuStrip = contextMenuProgram;
Type t = typeof(NotifyIcon);
MethodInfo mi = t.GetMethod("ShowContextMenu", BindingFlags.NonPublic | BindingFlags.Instance);
mi.Invoke(this.programEntry, null);
}
else if (e.Button == MouseButtons.Right)
{
    this.programEntry.ContextMenuStrip = contextMenuMenu;
Type t = typeof(NotifyIcon);
MethodInfo mi = t.GetMethod("ShowContextMenu", BindingFlags.NonPublic | BindingFlags.Instance);
mi.Invoke(this.programEntry, null);
}
}首先判断鼠标的mousedown事件由左键还是右键触发,然后动态改变小图标的contextmenustrip属性,通过反射重新调用ShowContextMenu方法,完美实现contextmenustrip效果。
二、全局钩子
通过系统钩子捕获鼠标事件,再判断鼠标输入消息,鼠标输入消息 -- msdn文档,捕获左键点击事件,添加执行方法,原论坛地址
这是调用钩子操作的执行类 public class MouseHook{

private Point point;
private Point Point{

get { return point; }
set
{
if (point != value)
{
point = value;
}
}
}

private int hHook;

public const int WH_MOUSE_LL = 14;
public const int VM_MOUSEMOVE = 0x0200;
public const int VM_LBUTTONDOWN = 0x0201;

public Win32Api.HookProc hProc;

public MouseHook() { this.Point = new Point(); }

public int SetHook()

{

hProc = new Win32Api.HookProc(MouseHookProc);

hHook = Win32Api.SetWindowsHookEx(WH_MOUSE_LL, hProc, IntPtr.Zero, 0);

return hHook;

}

public void UnHook()

{

Win32Api.UnhookWindowsHookEx(hHook);

}

private int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)

{

Win32Api.MouseHookStruct MyMouseHookStruct = (Win32Api.MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(Win32Api.MouseHookStruct));

if (nCode < 0)

{

return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);

}

else

{
this.Point = new Point(MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y);
switch ((int)wParam)
{
case VM_MOUSEMOVE:

if (MouseMoveEvent != null)
{
var e = new MouseEventArgs(MouseButtons.None, 0, this.Point.X, this.Point.Y, 0);
MouseMoveEvent(this, e);
}
break;
case VM_LBUTTONDOWN:
if (MouseMousedownEvent != null)
{
var e = new MouseEventArgs(MouseButtons.Left, 0, this.Point.X, this.Point.Y, 0);
MouseMousedownEvent(this, e);
}
break;
}
return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);
}

}

//委托+事件(把钩到的消息封装为事件,由调用者处理)

public delegate void MouseMoveHandler(object sender, MouseEventArgs e);

public event MouseMoveHandler MouseMoveEvent;
public event MouseMoveHandler MouseMousedownEvent;

}这是钩子的直接操作类
public class Win32Api

{
[StructLayout(LayoutKind.Sequential)]

public class POINT

{
public int x;

public int y;

}

[StructLayout(LayoutKind.Sequential)]

public class MouseHookStruct

{

public POINT pt;

public int hwnd;

public int wHitTestCode;

public int dwExtraInfo;

}

public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

//安装钩子

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

//卸载钩子

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

public static extern bool UnhookWindowsHookEx(int idHook);

//调用下一个钩子

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

}自定义了两个常量
public const int VM_MOUSEMOVE = 0x0200;
public const int VM_LBUTTONDOWN = 0x0201;
一个512,一个513,,对应鼠标移动事件和鼠标左键按下事件,在构造函数或者load中实例化钩子操作,绑定自定义函数MouseHook mh = new MouseHook();
mh.SetHook();
mh.MouseClickEvent += mh_MouseClickEvent;然后自定义一个函数,判断鼠标点击的位置,如果不在两个contextmenustrip空间的位置范围内,则隐藏这两个菜单 void MouseMousedownEvent(object sender, EventArgs e)
{
    if (!(this.contextMenuProgram.Bounds.Contains(PointToClient(Control.MousePosition)) || this.contextMenuMenu.Bounds.Contains(PointToClient(Control.MousePosition)))
&& Control.MouseButtons == MouseButtons.None)
{
this.contextMenuProgram.Visible = false;
    this.contextMenuMenu.Visible = false;
}
这种做法还是菜单的效果能够实现的,但是每次鼠标移到二级菜单展开的时候总是报错,要不空指针异常,要不报错:托管调试助手 "CallbackOnCollectedDelegate":“对“wnmp!GlobalMouseHock.Win32Api+HookProc::Invoke”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃、损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们“。我对C#了解甚少,还望大神指教。
三、时钟
第二个方法是用系统的消息机制对全局的鼠标事件进行捕获,第三个方法是用时钟循环查询鼠标位置,判断此次点击是否隐藏。
首先在构造函数或者load中拉取一个时钟Timer timer1 = new Timer();
timer1.Interval = 100;
timer1.Enabled = true;
timer1.Tick += new EventHandler(T_Tick);//添加事件然后自定义触发事件void T_Tick(object sender, EventArgs e)
{
    if (!(this.contextMenuProgram.Bounds.Contains(PointToClient(Control.MousePosition)) || this.contextMenuMenu.Bounds.Contains(PointToClient(Control.MousePosition)))
    && Control.MouseButtons == MouseButtons.Left)
{
this.contextMenuProgram.Visible = false;
this.contextMenuMenu.Visible = false;
}
}经过测试也是可以实现的,但是灵敏度并不高,而且位置有偏差,需要手动调整,如果菜单有所改变则需要在改这个位置的代码,很不方便。
综上所述,第一种办法是我力所能及的找到的最好的办法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐