9

WinFormsでは、を呼び出してDoDragDropアイテムのドラッグを開始した後、コントロールはマウスホイールでスクロールしなくMouseWheelなり、ユーザーがドラッグしているものをドロップするまで、コントロールのイベントは呼び出されなくなります。

ドラッグ中にマウスホイールを機能させる方法はありますか?

4

4 に答える 4

6

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

このコードは、組み込みのWindowsフォームコントロールクラスからのイベントのように動作LowLevelMouseHookするイベントをクラスに提供します。MouseWheel

WindowsHooks(さらに、コードは他のフックで使用するための抽象クラスSafeHookHandle、ハンドルが解放されることを保証するクラス、およびネイティブメソッドのヘルパークラスに分割されます)

この背後にあるテクニックを見て理解する必要がありますSetWindowsHookExCALLBACK LowLevelMouseProc


このイベントは、アプリケーションに限定されるものではなく、フォームの外でマウスをキャプチャするため、ローカルイベントを使用できない操作でも機能するはずです。

于 2011-03-16T17:04:33.440 に答える
4

いいえ、D + D中に識別可能なフォーカスはなく、D+Dイベントはマウスホイールの動きを報告しません。典型的なトリックは、DragOverを使用して、ドラッグカーソルがスクロール可能な領域のいずれかの端に近いかどうかを確認することです。そしてタイマーでスクロールします。例はここにあります

于 2011-01-27T19:53:52.667 に答える
2

組み込みの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カーソルを使用するのではなく、ドラッグアンドドロップを示すものを使用する必要があります。このサンプルは、APIブラックボックスをオーバーライドするよりも独自のD+Dを管理する方が簡単な場合があることを示しています。

于 2011-03-14T20:53:20.207 に答える
1

これはどう:

目的のDataGrid(ドロップすると想定されるもの)では、マウスポインターが最後または最初に到達すると、下または上にスクロールし始めます(もちろん、mousein / mouseoutイベントを制御します)。

オブジェクトをExcelでドラッグしてみてください。表示されているものの最後/最初に到達すると、下/上にスクロールし始めます。

私が自分自身を説明するかどうかわからない、私に知らせてください、そして私はそれをより明確にしようとします

于 2011-03-16T16:18:21.723 に答える