鼠标滚轮事件与hover控件一起使用

在我的C#3.5 Windows窗体应用程序中,我有一些SplitContainers。 每个内部都有一个列表控件(dock fill)。 当焦点位于其中一个控件上并移动鼠标滚轮时,滚动显示现在聚焦的列表。

我的任务是滚动当前由鼠标hover的列表,而不是选中的列表。 是否可以在Windows窗体中? 如果没有,是否可以使用PInvoke?

看起来你可以使用IMessageFilter和PInvoke来处理这个问题。 可以在将鼠标滚轮事件重定向到未聚焦的Windows窗体控件中找到VB中的示例。 您应该能够轻松地将其转换为C#。

兴趣点

此类对给定任务使用以下技术:

  • 监听控件的MouseEnter和MouseLeave事件,以确定鼠标指针何时在控件上。
  • 实现IMessageFilter以捕获应用程序中的WM_MOUSEWHEEL消息。
  • PInvoke Windows API调用SendMessage将WM_MOUSEWHEEL消息重定向到控件的句柄。
  • IMessageFilter对象实现为MouseWheelRedirector类的单例,并由共享成员Attach,Detach和Active访问。

使用VB.NET到C#转换器 ,这是你最终得到的:

 using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Windows.Forms; using System.Runtime.InteropServices; public class MouseWheelRedirector : IMessageFilter { private static MouseWheelRedirector instance = null; private static bool _active = false; public static bool Active { get { return _active; } set { if (_active != value) { _active = value; if (_active) { if (instance == null) { instance = new MouseWheelRedirector(); } Application.AddMessageFilter(instance); } else { if (instance != null) { Application.RemoveMessageFilter(instance); } } } } } public static void Attach(Control control) { if (!_active) Active = true; control.MouseEnter += instance.ControlMouseEnter; control.MouseLeave += instance.ControlMouseLeaveOrDisposed; control.Disposed += instance.ControlMouseLeaveOrDisposed; } public static void Detach(Control control) { if (instance == null) return; control.MouseEnter -= instance.ControlMouseEnter; control.MouseLeave -= instance.ControlMouseLeaveOrDisposed; control.Disposed -= instance.ControlMouseLeaveOrDisposed; if (object.ReferenceEquals(instance.currentControl, control)) instance.currentControl = null; } private MouseWheelRedirector() { } private Control currentControl; private void ControlMouseEnter(object sender, System.EventArgs e) { var control = (Control)sender; if (!control.Focused) { currentControl = control; } else { currentControl = null; } } private void ControlMouseLeaveOrDisposed(object sender, System.EventArgs e) { if (object.ReferenceEquals(currentControl, sender)) { currentControl = null; } } private const int WM_MOUSEWHEEL = 0x20a; public bool PreFilterMessage(ref System.Windows.Forms.Message m) { if (currentControl != null && m.Msg == WM_MOUSEWHEEL) { SendMessage(currentControl.Handle, m.Msg, m.WParam, m.LParam); return true; } else { return false; } } [DllImport("user32.dll", SetLastError = false)] private static extern IntPtr SendMessage( IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); } 

我有类似的问题,并找到了这个post……所以发布我的迟来的答案给其他可能找到这个post的人。 在我的情况下,我只是希望鼠标滚轮事件转到光标下的任何控件…就像右键单击一样(如果右键单击转到焦点控件而不是光标下的控件,那将会很困惑……我认为鼠标轮也是如此,除了我们已经习惯了它。

无论如何,答案非常简单。 只需将PreFilterMessage添加到您的应用程序中,并将鼠标滚轮事件重定向到鼠标下的控件:

  public bool PreFilterMessage(ref Message m) { switch (m.Msg) { case WM_MOUSEWHEEL: // 0x020A case WM_MOUSEHWHEEL: // 0x020E IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam)); if (hControlUnderMouse == m.HWnd) return false; // already headed for the right control else { // redirect the message to the control under the mouse SendMessage(hControlUnderMouse, m.Msg, m.WParam, m.LParam); return true; } default: return false; } } 

使用Control.MouseEnter Event将焦点设置为控件。 例如,使用ActiveControl属性

这是Brian KennedyHank Schultz评论中完成的答案:

首先,你应该让一个类实现IMessageFilter

 public class MessageFilter : IMessageFilter { private const int WM_MOUSEWHEEL = 0x020A; private const int WM_MOUSEHWHEEL = 0x020E; [DllImport("user32.dll")] static extern IntPtr WindowFromPoint(Point p); [DllImport("user32.dll", CharSet = CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); public bool PreFilterMessage(ref Message m) { switch (m.Msg) { case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam)); if (hControlUnderMouse == m.HWnd) { //Do nothing because it's already headed for the right control return false; } else { //Send the scroll message to the control under the mouse uint u = Convert.ToUInt32(m.Msg); SendMessage(hControlUnderMouse, u, m.WParam, m.LParam); return true; } default: return false; } } } 

用法示例:

 public partial class MyForm : Form { MessageFilter mf = null; private void MyForm_Load(object sender, EventArgs e) { mf= new MessageFilter(); Application.AddMessageFilter(mf); } private void MyForm_FormClosing(object sender, FormClosingEventArgs e) { Application.RemoveMessageFilter(mf); } }