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