c# winforms user-controls mousewheel

c# - UserControl: ¿Cómo agregar MouseWheel Listener?



winforms user-controls (4)

Estoy creando un UserControl que debería reaccionar si el mouse está sobre el control y MouseWheel se gira.

Actualmente estoy haciendo esto como se muestra aquí:

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

De hecho, esto funciona bien, pero el problema es la parte con "this.Focus ();" ya que destruye mi comportamiento de formas / aplicaciones.

¿Hay una mejor manera de lograr esto?


teniendo el mismo problema, finalmente implementé una combinación de ambas soluciones por @Paul_Westcott y @ nr1. Esta es una solución local, como la solución de @Paul_Westcott (solo se aplica al control de winforms que se suscribe). Es un monitor múltiple seguro y seguro para MDI (se superpone con otras ventanas dentro de la aplicación)

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

Una vez que haya agregado esta clase de ayuda en su solución, suscribirá un control myControl a la rueda del mouse, en una línea, de la siguiente manera:

public void Init() { MouseWheelHandlerForWinformsControl.MemorySafeAdd(myControl, OnMouseWheelEvent); } void OnMouseWheelEvent(MouseEventArgs args) { // do what you need here }


Copie pegado de 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); } }

esto funciona genial, thx Hans Passant


Solo una ligera variación de la solución que ya se ha publicado, para no tener que usar P / Invoke.

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

Y luego esto puede usarse desde controles individuales como

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


La respuesta de Paul Westcott funciona muy bien cuando se usa un FlowLayoutPanel. Mi implementación del evento MyOnMouseWheel es:

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