2011-10-21 63 views
3

我正在创建一个UserControl,它应该在鼠标悬停在控件上并且MouseWheel旋转时发生反应。UserControl:如何添加MouseWheel侦听器?

目前我做这如下所示:

public MyUserControl() 
    { 
     this.MouseWheel += new MouseEventHandler(MouseWheelHandler); 
    } 

    private void MouseWheelHandler(object sender, System.Windows.Forms.MouseEventArgs e) 
    { 
     if (e.Delta > 0) 
      incIndex(); 

     if (e.Delta < 0) 
      decIndex(); 
    } 

    protected override void OnMouseEnter(EventArgs e) 
    { 
     this.Focus(); 

     base.OnMouseEnter(e); 
    } 

其实这工作得很好,但问题是与部分“this.Focus();”因为它破坏了我的表单/应用程序行为。

有没有更好的方式来实现这一目标?

+0

为什么你需要关注你的控制? – dknaack

+4

http://social.msdn.microsoft.com/forums/en-US/winforms/thread/eb922ed2-1036-41ca-bd15-49daed7b637c/ –

+0

@dknaack:因为否则它根本不工作;) – nr1

回答

3

复制从http://social.msdn.microsoft.com/forums/en-US/winforms/thread/eb922ed2-1036-41ca-bd15-49daed7b637c/

using System; 
using System.ComponentModel; 
using System.Drawing; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

namespace WindowsApplication1 { 
    public partial class Form1 : Form, IMessageFilter { 
    public Form1() { 
     InitializeComponent(); 
     Application.AddMessageFilter(this); 
    } 

    public bool PreFilterMessage(ref Message m) { 
     if (m.Msg == 0x20a) { 
     // WM_MOUSEWHEEL, find the control at screen position m.LParam 
     Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16); 
     IntPtr hWnd = WindowFromPoint(pos); 
     if (hWnd != IntPtr.Zero && hWnd != m.HWnd && Control.FromHandle(hWnd) != null) { 
      SendMessage(hWnd, m.Msg, m.WParam, m.LParam); 
      return true; 
     } 
     } 
     return false; 
    } 

    // P/Invoke declarations 
    [DllImport("user32.dll")] 
    private static extern IntPtr WindowFromPoint(Point pt); 
    [DllImport("user32.dll")] 
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); 
    } 
} 

粘贴这对已经公布,为了不必须使用的P/Invoke的解决方案的伟大工程,THX汉斯帕桑特

+0

警告:'新点(m.LParam.ToInt32()&0xffff,m.LParam.ToInt32()>> 16)'是越野车。错误发生在负坐标上!每个论点都必须首先简短。最简单的方法是使用专用的'点(INT)'构造函数如下:'新点(m.LParam.ToInt32()' – jeromerg

2

只是一个微小的变化。

public static class MouseWheelHandler 
{ 
    public static void Add(Control ctrl, Action<MouseEventArgs> onMouseWheel) 
    { 
     if (ctrl == null || onMouseWheel == null) 
      throw new ArgumentNullException(); 

     var filter = new MouseWheelMessageFilter(ctrl, onMouseWheel); 
     Application.AddMessageFilter(filter); 
     ctrl.Disposed += (s, e) => Application.RemoveMessageFilter(filter); 
    } 

    class MouseWheelMessageFilter 
     : IMessageFilter 
    { 
     private readonly Control _ctrl; 
     private readonly Action<MouseEventArgs> _onMouseWheel; 

     public MouseWheelMessageFilter(Control ctrl, Action<MouseEventArgs> onMouseWheel) 
     { 
      _ctrl = ctrl; 
      _onMouseWheel = onMouseWheel; 
     } 

     public bool PreFilterMessage(ref Message m) 
     { 
      var parent = _ctrl.Parent; 
      if (parent != null && m.Msg == 0x20a) // WM_MOUSEWHEEL, find the control at screen position m.LParam 
      { 
       var pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16); 

       var clientPos = _ctrl.PointToClient(pos); 

       if (_ctrl.ClientRectangle.Contains(clientPos) 
       && ReferenceEquals(_ctrl, parent.GetChildAtPoint(parent.PointToClient(pos)))) 
       { 
        var wParam = m.WParam.ToInt32(); 
        Func<int, MouseButtons, MouseButtons> getButton = 
         (flag, button) => ((wParam & flag) == flag) ? button : MouseButtons.None; 

        var buttons = getButton(wParam & 0x0001, MouseButtons.Left) 
           | getButton(wParam & 0x0010, MouseButtons.Middle) 
           | getButton(wParam & 0x0002, MouseButtons.Right) 
           | getButton(wParam & 0x0020, MouseButtons.XButton1) 
           | getButton(wParam & 0x0040, MouseButtons.XButton2) 
           ; // Not matching for these /*MK_SHIFT=0x0004;MK_CONTROL=0x0008*/ 

        var delta = wParam >> 16; 
        var e = new MouseEventArgs(buttons, 0, clientPos.X, clientPos.Y, delta); 
        _onMouseWheel(e); 

        return true; 
       } 
      } 
      return false; 
     } 
    } 
} 

然后这可以从个人控制使用像

class MyControl 
    : Control 
{ 
    public MyControl() 
    { 
     ... 
     MouseWheelHandler.Add(this, MyOnMouseWheel); 
    } 

    void MyOnMouseWheel(MouseEventArgs e) 
    { 
     ... 
    } 
} 
+0

警告:'新点(m.LParam.ToInt32()0xFFFF的,m.LParam .ToInt32()>> 16)'是bug。错误发生在负坐标上!每个参数必须首先被转换为short。最简单的方法是使用专用的'Point(int)'构造函数,如下所示:'new Point(m .LParam.ToInt32())' – jeromerg

+0

警告:因为它滚动即使鼠标是窗口上方重叠控制(MDI窗口)这种解决方案不能与MDI工作 – jeromerg

0

保罗·韦斯科特的答案使用FlowLayoutPanel中时的伟大工程。我MyOnMouseWheel事件的实现是:有同样的问题

void MyOnMouseWheel(MouseEventArgs e) 
{ 
    int ChangeIncrement = (this.panel1.VerticalScroll.SmallChange * 4); //Change the 4 to any positive number to scroll more or less one each scroll event. 
    if (e.Delta < 0) 
    { 
     int NewValue = this.panel1.VerticalScroll.Value + ChangeIncrement; 
     if (NewValue > this.panel1.VerticalScroll.Maximum) 
     { 
      this.panel1.VerticalScroll.Value = this.panel1.VerticalScroll.Maximum; 
     } 
     else 
     { 
      this.panel1.VerticalScroll.Value = NewValue; 
     } 
    } 
    else if (e.Delta > 0) 
    { 
     int NewValue = this.panel1.VerticalScroll.Value - ChangeIncrement; 
     if (NewValue < this.panel1.VerticalScroll.Minimum) 
     { 
      this.panel1.VerticalScroll.Value = this.panel1.VerticalScroll.Minimum; 
     } 
     else 
     { 
      this.panel1.VerticalScroll.Value = NewValue; 
     } 
    } 
    this.panel1.PerformLayout(); 
} 
+0

这个代码可以使用一些优化!例如:NewValue = this.panel1.VerticalScroll.Value +((e.Delta> 0)? - 1:1)* ChangeIncrement; – Zuoanqh

+0

this.panel1.VerticalScroll.Value = max(this.panel1.VerticalScroll.Minimum,min(this.panel1.VerticalScroll.Maximum,NewValue)) – Zuoanqh

+0

并附加if(e.Delta == 0)return;这可能会减少整个IF树到*咳嗽*三行代码。 – Zuoanqh

5

,我终于实现双方溶液@Paul_Westcott和@ NR1的混合。这是类似@Paul_Westcott解决方案的本地解决方案(仅适用于订阅的winforms控件)。这是多监视器安全和MDI安全(被其他窗口重叠在应用程序内)

public static class MouseWheelHandlerForWinformsControl 
{ 
    private class MouseWheelMessageFilter : IMessageFilter 
    { 
     [DllImport("user32.dll")] 
     private static extern IntPtr WindowFromPoint(Point pt); 

     private readonly Control mCtrl; 
     private readonly Action<MouseEventArgs> mOnMouseWheel; 

     public MouseWheelMessageFilter(Control ctrl, Action<MouseEventArgs> onMouseWheel) 
     { 
      mCtrl = ctrl; 
      mOnMouseWheel = onMouseWheel; 
     } 

     public bool PreFilterMessage(ref Message m) 
     { 
      // handle only mouse wheel messages 
      if (m.Msg != 0x20a) 
       return false; 

      Point mouseAbsolutePosition = new Point(m.LParam.ToInt32()); 
      Point mouseRelativePosition = mCtrl.PointToClient(mouseAbsolutePosition); 

      IntPtr hControlUnderMouse = WindowFromPoint(mouseAbsolutePosition); 
      Control controlUnderMouse = Control.FromHandle(hControlUnderMouse); 

      if (controlUnderMouse != mCtrl) 
       return false; 

      MouseButtons buttons = GetMouseButtons(m.WParam.ToInt32()); 
      int delta = m.WParam.ToInt32() >> 16; 

      var e = new MouseEventArgs(buttons, 0, mouseRelativePosition.X, mouseRelativePosition.Y, delta); 

      mOnMouseWheel(e); 

      return true; 
     } 

     private static MouseButtons GetMouseButtons(int wParam) 
     { 
      MouseButtons buttons = MouseButtons.None; 

      if(HasFlag(wParam, 0x0001)) buttons |= MouseButtons.Left; 
      if(HasFlag(wParam, 0x0010)) buttons |= MouseButtons.Middle; 
      if(HasFlag(wParam, 0x0002)) buttons |= MouseButtons.Right; 
      if(HasFlag(wParam, 0x0020)) buttons |= MouseButtons.XButton1; 
      if(HasFlag(wParam, 0x0040)) buttons |= MouseButtons.XButton2; 

      return buttons; 
     } 

     private static bool HasFlag(int input, int flag) 
     { 
      return (input & flag) == flag; 
     } 
    } 

    public static void MemorySafeAdd(Control ctrl, Action<MouseEventArgs> onMouseWheel) 
    { 
     if (ctrl == null || onMouseWheel == null) 
      throw new ArgumentNullException(); 

     var filter = new MouseWheelMessageFilter(ctrl, onMouseWheel); 
     Application.AddMessageFilter(filter); 
     ctrl.Disposed += (s, e) => Application.RemoveMessageFilter(filter); 
    } 
} 

一旦你加入这个助手类到您的解决方案,你订阅控制myControl的鼠标滚轮,在一个行,如下:

public void Init() { 
    MouseWheelHandlerForWinformsControl.MemorySafeAdd(myControl, OnMouseWheelEvent); 
} 

void OnMouseWheelEvent(MouseEventArgs args) { 
    // do what you need here 
} 
+0

不敢相信我是第一个给它投票的!这个解决方案是独立的,只需要添加一行代码,并避免了其他解决方案中的问题。但是如果你也给它一行代码来使用它,比如“MouseWheelHandlerForWinformsControl.MemorySafeAdd(your_control,YourControl_MouseWheel);” – Zuoanqh

+0

@Zananqh感谢您的反馈。实际上,它是我们软件中使用的代码,并且可以工作(并且已经在多屏幕和MDI环境中进行了测试)。相应地对您的评论进行了升级。 – jeromerg

+1

哦,你必须爱上互联网:-)工作中的某个人已经在我们的代码库中修复了MDI的事情,但是随后有人开始抱怨MDI的事情,然后我回到了这里,你已经完成了工作我:-)良好的工作@jeromerg! –