2012-06-14 18 views
9

En mi aplicación C# 3.5 Windows Forms, tengo algunos SplitContainers. Hay un control de lista dentro de cada (relleno de muelle). Cuando el foco está en uno de estos controles y muevo la rueda del mouse, la lista, que ahora está enfocada, se desplaza.Evento de la rueda del mouse para trabajar con control suspendido

Mi tarea consiste en desplazar la lista, que está desplazada por el mouse, no la que está seleccionada. ¿Es posible en Windows Forms? Si no, ¿es posible con PInvoke?

+0

Parece que hicieron "desplazarse independientemente del cursor del mouse" sobre el comportamiento estándar en Windows 10. Lo cual es un poco molesto en la mayoría de los casos, en realidad. – Nyerguds

Respuesta

9

Parece que puede usar el IMessageFilter y PInvoke para manejar esto. Un ejemplo en VB se puede encontrar en Redirect Mouse Wheel Events to Unfocused Windows Forms Controls. Debería poder convertir esto fácilmente a C#.

Puntos de Interés

Esta clase se utilizan las siguientes técnicas para la tarea encomendada:

  • escuchar a eventos MouseEnter y MouseLeave del control para determinar cuando el puntero del ratón está sobre el control.
  • Implemente IMessageFilter para capturar mensajes WM_MOUSEWHEEL en la aplicación.
  • PInvoque la llamada a la API de Windows SendMessage redirigiendo el mensaje WM_MOUSEWHEEL al control del controlador.
  • El objeto IMessageFilter se implementa como un singleton de la clase MouseWheelRedirector y se accede por los miembros compartidos Attach, Detach y Active.

Usando a VB.NET to C# converter, esto es lo que usted termina con:

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

Esto no parece funcionar con la mayoría de las almohadillas táctiles comunes utilizando métodos como desplazamiento hacia la derecha o doble dedo. Parece que sus controladores hacen magia negra para enviar los mensajes de desplazamiento directamente a las barras de desplazamiento del control en lugar de enviar 'WM_MOUSEWHEEL' al control como deberían. ¿Alguna idea sobre cómo evitar eso? – Nyerguds

+0

Hmm. Parece que NumericUpDown no quiere escuchar esto. – Nyerguds

5

tuve pregunta similar y encontrado este hilo ... lo que la publicación mi respuesta tardía para otros que podrían encontrar este hilo. En mi caso, solo quiero que los eventos de la rueda del mouse vayan al control que esté debajo del cursor ... igual que al hacer clic con el botón derecho (sería desconcertante si el clic derecho fuera al control de foco en vez del control debajo del cursor ... Sostengo que lo mismo es cierto para la rueda del mouse, excepto que ya nos hemos acostumbrado).

De todos modos, la respuesta es muy fácil. Sólo tiene que añadir un PreFilterMessage a su aplicación y haga que sea redirigir eventos de rueda del ratón para el control bajo el ratón:

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

Falta un poco de esto. Necesita DllImport WindowFromPoint() y SendMessage: '[DllImport (" user32.dll ")] static extern IntPtr WindowFromPoint (Punto p); [DllImport (" user32.dll ", CharSet = CharSet.Auto)] static extern IntPtr SendMessage (IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); ' Además, PreFilterMessage() proviene de IMessageFilter, y esa implementación debe pasarse a' ApplicationAddMessageFilter() '. Una vez hecho esto, todos los paneles de mi aplicación se pueden desplazar con el mouse. Sin embargo, hacer doble clic ya no resalta el texto. Impar. –

2

Ésta es Brian respuesta Kennedy 's completado con Hank Schultz comentario:

Primera usted debe hacer una clase implementa 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; 
     } 
    } 
} 

Ejemplo de uso:

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