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);
}自定义了两个常量
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;
}
}经过测试也是可以实现的,但是灵敏度并不高,而且位置有偏差,需要手动调整,如果菜单有所改变则需要在改这个位置的代码,很不方便。
综上所述,第一种办法是我力所能及的找到的最好的办法。
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;
}
}经过测试也是可以实现的,但是灵敏度并不高,而且位置有偏差,需要手动调整,如果菜单有所改变则需要在改这个位置的代码,很不方便。
综上所述,第一种办法是我力所能及的找到的最好的办法。
相关文章推荐
- 我创建了一个托盘图标,可以正常使用,点击右键打开菜单。问题是如果点击右键后不选择其中一个菜单项进行操作的话,它就总不消失。
- 系统托盘图标显示菜单(TrackPopupMenu)无法自动消失解决方法
- WinForm 之 窗口最小化到托盘及右键图标显示菜单
- 托盘图标菜单弹出后,点击其它地方,菜单不消失的问题
- WinForm窗口最小化到系统托盘右击托盘图标弹出退出菜单
- 让一个程序托盘显示,并且右键托盘图标可以弹出菜单
- Android 自定义对话框,可设置大小和显示位置,并设置点击其他位置不消失
- MFC 托盘右键菜单点击非菜单区域后消失
- 让WinForm应用程序最小化图标显示在任务栏并提供右击菜单
- 显示scrollview时对滚动条进行任意位置的定位
- 今天论坛中遇到的两则问题说明(COM库的初始化问题以及托盘图标弹出菜单不消失问题)
- 百度地图点击地图显示所选位置与自定义回到定位按钮
- C# WinForm窗体任意位置点击事件
- 在任意位置点击图片显示
- 显示scrollview时对滚动条进行任意位置的定位 .
- 让WinForm应用程序最小化图标显示在任务栏并提供右击菜单
- popwindow 在安卓7.0上显示位置错误以及在6.0下点击外部不能消失的情况解析
- 显示scrollview时对滚动条进行任意位置的定位 .
- scrollview显示到任意位置的定位
- Android中使用百度API定位,并实现手势操作(显示最后点击地图的位置)