2011-01-27 11 views
9

En WinForms, después de llamar a DoDragDrop para comenzar a arrastrar un elemento, los controles ya no se desplazan con la rueda del mouse, y el evento MouseWheel del control ya no se llama, hasta que el usuario descarta lo que está arrastrando.¿Se puede usar la rueda del mouse al arrastrar/soltar?

¿Hay alguna manera de hacer que la rueda del mouse funcione mientras se arrastra?

+0

hey No estoy seguro de esto ya que acabo de encontrarlo hoy. Pero, ¿sería posible hacer lo que le pides a Rx? –

+0

¿Puedes verificar y ver si los eventos 'MouseWheel' se están entregando a la * fuente de arrastre * en lugar del control sobre el que ha pasado el mouse? –

+0

Por lo tanto, desea colocar un objeto en forma de rollos o un objeto más grande que no se ajuste a ningún formulario. Estoy en lo correcto ? –

Respuesta

6

Usted podría conseguir un mundial MouseWheel con un gancho de teclado.

using System; 
using System.ComponentModel; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 
using Microsoft.Win32.SafeHandles; 

using BOOL = System.Boolean; 
using DWORD = System.UInt32; 
using HHOOK = SafeHookHandle; 
using HINSTANCE = System.IntPtr; 
using HOOKPROC = HookProc; 
using LPARAM = System.IntPtr; 
using LRESULT = System.IntPtr; 
using POINT = System.Drawing.Point; 
using ULONG_PTR = System.IntPtr; 
using WPARAM = System.IntPtr; 

public delegate LRESULT HookProc(int nCode, WPARAM wParam, LPARAM lParam); 

internal static class NativeMethods 
{ 
    [DllImport("User32.dll", SetLastError = true)] 
    internal static extern HHOOK SetWindowsHookEx(
     HookType idHook, 
     HOOKPROC lpfn, 
     HINSTANCE hMod, 
     DWORD dwThreadId); 

    [DllImport("User32.dll")] 
    internal static extern LRESULT CallNextHookEx(
     HHOOK hhk, 
     int nCode, 
     WPARAM wParam, 
     LPARAM lParam); 

    [DllImport("User32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    internal static extern BOOL UnhookWindowsHookEx(
     IntPtr hhk); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
    public static extern IntPtr GetModuleHandle(string lpModuleName); 
} 

internal static class NativeTypes 
{ 
    internal enum MSLLHOOKSTRUCTFlags : uint 
    { 
     LLMHF_INJECTED = 0x00000001U, 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct MSLLHOOKSTRUCT 
    { 
     internal POINT pt; 
     internal DWORD mouseData; 
     internal MSLLHOOKSTRUCTFlags flags; 
     internal DWORD time; 
     internal ULONG_PTR dwExtraInfo; 
    } 
} 

internal static class NativeConstants 
{ 
    internal const int WH_MOUSE_LL = 14; 

    internal const int HC_ACTION = 0; 

    internal const int WM_MOUSEWHEEL = 0x020A; 
    internal const int WM_MOUSEHWHEEL = 0x020E; 

    internal const int WHEEL_DELTA = 120; 
} 

public enum HookType 
{ 
    LowLevelMouseHook = NativeConstants.WH_MOUSE_LL 
} 

public enum HookScope 
{ 
    LowLevelGlobal, 
} 

public class SafeHookHandle : SafeHandleZeroOrMinusOneIsInvalid 
{ 
    private SafeHookHandle() : base(true) { } 

    public static SafeHookHandle SetWindowsHook(HookType idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId) 
    { 
     var hhk = NativeMethods.SetWindowsHookEx(idHook, lpfn, hMod, dwThreadId); 

     if(hhk.IsInvalid) 
     { 
      throw new Win32Exception(); 
     } 
     else 
     { 
      return hhk; 
     } 
    } 

    public IntPtr CallNextHook(int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     return NativeMethods.CallNextHookEx(this, nCode, wParam, lParam); 
    } 

    protected override bool ReleaseHandle() 
    { 
     return NativeMethods.UnhookWindowsHookEx(this.handle); 
    } 
} 

public abstract class WindowsHook : IDisposable 
{ 
    private SafeHookHandle hhk; 
    private HookProc lpfn; 

    protected WindowsHook(HookType idHook, HookScope scope) 
    { 
     this.lpfn = this.OnWindowsHook; 

     switch(scope) 
     { 
      case HookScope.LowLevelGlobal: 
       IntPtr moduleHandle = NativeMethods.GetModuleHandle(null); 
       this.hhk = SafeHookHandle.SetWindowsHook(idHook, this.lpfn, moduleHandle, 0U); 
       return; 
      default: 
       throw new InvalidEnumArgumentException("scope", (int)scope, typeof(HookScope)); 
     } 
    } 

    protected virtual IntPtr OnWindowsHook(int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     return this.hhk.CallNextHook(nCode, wParam, lParam); 
    } 

    public void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if(disposing) 
     { 
      if(this.hhk != null) { this.hhk.Dispose(); } 
     } 
    } 
} 

public class LowLevelMouseHook : WindowsHook 
{ 
    public event MouseEventHandler MouseWheel; 

    public LowLevelMouseHook() : base(HookType.LowLevelMouseHook, HookScope.LowLevelGlobal) { } 

    protected sealed override IntPtr OnWindowsHook(int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     if(nCode == NativeConstants.HC_ACTION) 
     { 
      var msLLHookStruct = (NativeTypes.MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(NativeTypes.MSLLHOOKSTRUCT)); 

      switch(wParam.ToInt32()) 
      { 
       case NativeConstants.WM_MOUSEWHEEL: 
       case NativeConstants.WM_MOUSEHWHEEL: 
        this.OnMouseWheel(new MouseEventArgs(Control.MouseButtons, 0, msLLHookStruct.pt.X, msLLHookStruct.pt.Y, (int)msLLHookStruct.mouseData >> 16)); 
        break; 
      } 
     } 

     return base.OnWindowsHook(nCode, wParam, lParam); 
    } 

    protected virtual void OnMouseWheel(MouseEventArgs e) 
    { 
     if(this.MouseWheel != null) 
     { 
      this.MouseWheel(this, e); 
     } 
    } 
} 

Uso de la muestra:

using (LowLevelMouseHook hook = new LowLevelMouseHook()) 
{ 
    hook.MouseWheel += (sender, e) => 
    { 
     Console.WriteLine(e.Delta); 
    }; 
    Application.Run(); 
} 

El código proporciona una clase LowLevelMouseHook con un evento MouseWheel que se comporta como el caso de las ventanas incorporadas forma clases de control.

(otra parte del código se divide en una clase abstracta WindowsHooks para su uso con otros ganchos, una clase SafeHookHandle para asegurar el mango se libera y las clases de ayuda para los métodos nativos)

usted debe buscar en SetWindowsHookEx y CALLBACK LowLevelMouseProc para entender la técnica detrás de esto.


Este evento no se limita a su aplicación, sino que también capturar el ratón fuera de su forma, por lo que también deben trabajar para sus operaciones en las que no se pueden utilizar los eventos locales.

+0

Hmm ... desafortunadamente, esto no parece funcionar: obtengo un * "No se puede establecer un gancho no local sin un identificador de módulo" * cuando intento iniciar el programa. Parece que 'SafeHookHandle.SetWindowsHook.hMod' no puede ser' IntPtr.Zero' ... –

+0

Al usar el resultado de 'GetModuleHandle (null)' en lugar de 'IntPtr.Zero' parece funcionar, lo editaré. Esto es exactamente lo que estaba buscando ... ¡Gracias! –

+0

De nada. Tuve problemas con Hat IntPtr.Zero, pero desaparecieron de repente (Win7 x64) y lo dejé. – riel

4

No, no hay un foco identificable durante el D + D y los eventos D + D no informan el movimiento de la rueda del mouse. Un truco típico es usar DragOver y verificar si el cursor de arrastre está cerca de cualquier extremo de una región desplazable. Y desplazarse con un temporizador. Un ejemplo is here.

+0

Sí, estoy haciendo eso, pero también quiero que la rueda del mouse funcione. Maldita sea, esperaba una solución fácil. Supongo que no sabes cómo hacerlo con eventos globales de mouse (a través de P/Invoke). –

+0

IMessageFilter puede hacerlo. Esa no es una solución fácil. –

+0

No, cancela eso, el foco está turbio. Muy tal vez. –

2

En lugar de utilizar la funcionalidad integrada de D + D e intentar anular su comportamiento con PInvoke y otros eventos, en su lugar podría crear su propio sistema arrastrar y soltar basado en eventos de ratón hacia abajo y hacia arriba que mantendrán el capacidades de desplazamiento de la rueda del mouse de la forma.

Aquí hay un ejemplo muy simple de una forma de prueba que contiene una etiqueta, que es la fuente de arrastre simulada ("activa" arrastrar el mouse hacia abajo) y un cuadro de lista poblado con elementos arbitrarios, que es la rueda del mouse desplazable soltar destino Si ejecuta una muestra como esta, notará que al cambiar el cursor en el evento del mouse en la etiqueta, al arrastrarlo sobre el cuadro de lista y luego al desplazarse con la rueda del mouse se comportará como se esperaba. El cuadro de lista se desplazará.

using System; 
using System.Windows.Forms; 

public partial class Form1 : Form { 
    public Form1() { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) { 
     for (int i=0; i<250; i++) listBox1.Items.Add("item " + i); 
    } 

    private void Label1MouseDown(object sender, MouseEventArgs e) { 
     Cursor.Current = Cursors.SizeAll; 
    } 
} 

Por supuesto, usted tiene que conectar su propia lógica para dejar caer artículos (como un controlador de ratón hacia arriba para definir el proceso de gota) y es probable que no desea utilizar el cursor SizeAll pero algo que es más indicativo de arrastrar y soltar. Esta muestra es solo para mostrar que administrar su propia D + D puede ser más simple que tratar de anular una blackbox API.

+0

No capturar el mouse es clave para hacer que esto funcione. –

+0

@Ben: Mi sugerencia en realidad no captura el mouse, solo cambia el cursor en un evento de mouse hacia abajo. El fragmento que ves es, literalmente, todo lo que se necesita para probar esto, aparte de todos los archivos basura generados y generados automáticamente, por supuesto. Todo lo que OP necesita hacer para completar esto es agregar una bandera, un manejador de mouse en el control apropiado y la lógica de lo que se supone que debe hacer la gota. –

+0

Exactamente. La diferencia entre su código y el código de marco es que el marco captura el mouse. –

1

¿Qué tal esto:

En la cuadrícula de datos objetivo (aquel en el que supone que la gota), cuando el puntero del ratón llega al final o al principio, se inicia el desplazamiento hacia abajo o hacia arriba (por supuesto que será el control de los eventos mousein/mouseout).

Trate de dibujar un objeto en excel, si llega al final/principio de lo que puede ver comenzará a desplazarse hacia abajo/arriba.

No sé si me explico, hágamelo saber y voy a tratar de hacer que sea más explícito

+0

Sí, ya lo estoy [haciendo eso] (http://stackoverflow.com/questions/2567809/how-to-autoscroll-a-datagridview-during-drag- and-drop/2568199 # 2568199) - pero eso es una solución, no una solución, para esta limitación. –

Cuestiones relacionadas