Можно ли использовать колесо мыши при перетаскивании? - PullRequest
9 голосов
/ 27 января 2011

В WinForms, после вызова DoDragDrop для начала перетаскивания элемента, элементы управления больше не прокручиваются колесом мыши, и событие MouseWheel элемента управления больше не вызывается, пока пользователь не отбросит все, что он перетаскивает.

Есть ли способ заставить колесо мыши работать при перетаскивании?

Ответы [ 4 ]

6 голосов
/ 16 марта 2011

Вы можете получить глобальный MouseWheel с помощью клавиатуры.

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

Пример использования:

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

Код предоставляет классу LowLevelMouseHook событие MouseWheel, которое ведет себя как событие из встроенных окон, формирует управляющие классы.

(Более того, код разбит на абстрактный класс WindowsHooks для использования с другими хуками, класс SafeHookHandle для обеспечения освобождения дескриптора и вспомогательные классы для нативных методов)

Вы должны взглянуть на SetWindowsHookEx и CALLBACK LowLevelMouseProc, чтобы понять технику, стоящую за этим.


Это событие не ограничивается вашим приложением, но также захватывает мышь вне вашей формы, поэтому оно также должно работать для ваших операций, где вы не можете использовать локальные события.

4 голосов
/ 27 января 2011

Нет, во время D + D нет опознаваемого фокуса, и события D + D не сообщают о движении колесика мыши назад.Типичный трюк использует DragOver и проверяет, находится ли перетаскивающий курсор рядом с любым концом прокручиваемой области.И прокручиваем с таймером.Пример здесь .

2 голосов
/ 14 марта 2011

Вместо того, чтобы использовать встроенную функциональность D + D и пытаться переопределить его поведение с помощью PInvoke и других событий, вы могли бы вместо этого создать свою собственную систему перетаскивания, основанную на событиях перемещения мыши вверх и вниз, которые сохранят форму возможности прокрутки колесиком мыши.

Вот очень простой пример из тестовой формы, которая содержит метку, которая является источником фиктивного перетаскивания («активирует» перетаскивание мыши) и список, заполненный произвольными элементами, который представляет собой каплю прокрутки колеса мыши. место назначения. Если вы запустите пример, подобный этому, вы заметите, что изменение курсора в событии мыши вниз на метке, перетаскивание его по списку и последующая прокрутка колесиком мыши будут вести себя как положено. Список будет прокручиваться.

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

Конечно, вы должны подключить свою собственную логику для отбрасывания элементов (например, обработчик мыши для определения процесса отбрасывания), и вы, вероятно, не хотите использовать курсор SizeAll, но что-то, что более показательно для перетаскивания и сбрасывание. Этот пример просто показывает, что управление собственным D + D может быть проще, чем попытка переопределить черный ящик API.

1 голос
/ 16 марта 2011

Как насчет этого:

В целевой DataGrid (та, где вы предполагаете отбросить), когда указатель мыши достигает конца или начала, вы начинаете прокручивать вниз или вверх (конечно, это будет контролировать события mousein / mouseout).

Попробуйте перетащить объект в Excel, если вы дойдете до конца / начинаете с того, что видите, он начнет прокручиваться вниз / вверх.

Я не знаю, объясню ли я, дайте мне знать, и я постараюсь сделать это более явным

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...