拖/放时可以使用鼠标滚轮吗?

发布于 2024-10-15 01:37:36 字数 146 浏览 8 评论 0原文

在 WinForms 中,调用 DoDragDrop 开始拖动项目后,控件不再使用鼠标滚轮滚动,并且不再调用控件的 MouseWheel 事件,直到用户放下他拖动的任何东西。

有没有办法让鼠标滚轮在拖动时工作?

In WinForms, after calling a DoDragDrop to start dragging an item, controls no longer scroll with the mouse-wheel, and the control's MouseWheel event is no longer called, until the user drops whatever he is dragging.

Is there a way to get the mouse wheel to work while dragging?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

剑心龙吟 2024-10-22 01:37:36

您可以获得带有键盘钩子的全局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();
}

该代码提供了一个带有事件 MouseWheel 的类 LowLevelMouseHook,该事件的行为类似于内置 Windows 窗体控件类中的事件。

(此外,代码被分为一个抽象类 WindowsHooks 以便与其他钩子一起使用,一个 SafeHookHandle 类以确保句柄被释放,以及用于本机方法的帮助器类)

您应该查看 SetWindowsHookEx回调LowLevelMouseProc 了解其背后的技术。


此事件不仅限于您的应用程序,还会捕获表单外部的鼠标,因此它也适用于您无法使用本地事件的操作。

You could get a global MouseWheel with a keyboard hook.

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

Sample Usage:

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

The code provides a class LowLevelMouseHook with an event MouseWheel that behaves like the event from the builtin windows forms control classes.

(Moreover the code is split into an abstract class WindowsHooks for use with other hooks, a SafeHookHandle class to ensure the handle is released and the helper classes for native methods)

You should look at SetWindowsHookEx and CALLBACK LowLevelMouseProc to understand the technique behind this.


This event is not limited to your application but will also capture the mouse outside your form, so it should also work for your operations where you can not use the local events.

碍人泪离人颜 2024-10-22 01:37:36

不,D+D 期间没有可识别的焦点,并且 D+D 事件不会报告鼠标滚轮运动。典型的技巧是使用 DragOver 并检查拖动光标是否靠近可滚动区域的任一端。并用计时器滚动。示例在这里。

No, there is no identifiable focus during the D+D and the D+D events don't report back mouse wheel motion. A typical trick is using DragOver and checking if the dragging cursor is close to either end of a scrollable region. And scroll with a timer. An example is here.

过期情话 2024-10-22 01:37:36

您可以基于鼠标向下和向上事件创建自己的拖放系统,而不是使用内置的 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 黑盒更简单。

Instead of using the built-in D+D functionality and trying to override its behavior with PInvoke and other events you could instead create your own drag and drop system based on mouse down and up events in a away that will retain the form's mouse-wheel scrolling capabilities.

Here is a very simple example from a test form that contains a label, which is the mock drag source ("activates" drag on mouse down), and a list box populated with arbitrary items, which is the mouse wheel scrollable drop destination. If you run a sample like this you'll notice that changing the cursor in the mouse down event on the label, dragging it over the list box and then scrolling with the mouse wheel will behave as expected. The list box will scroll.

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

Of course you have to wire up your own logic for dropping items (such as a mouse up handler to define the drop process) and you probably don't want to use the SizeAll cursor but something that is more indicative of dragging and dropping. This sample is just to show that managing your own D+D may be simpler than trying to override an API blackbox.

∞梦里开花 2024-10-22 01:37:36

怎么样:

在目标 DataGrid(您想要放置的位置)中,当鼠标指针到达末尾或开头时,您开始向下或向上滚动(当然它将控制 mousein/mouseout 事件)。

尝试在 Excel 中拖动一个对象,如果到达您所看到的内容的末尾/开头,它将开始向下/向上滚动。

我不知道我是否可以解释自己,请告诉我,我会尽力使其更明确

How about this:

In the objective DataGrid (the one where you suppose to drop), when the mouse pointer reaches the end or the begining, you start scrolling down or up (of course it will be controlling the mousein/mouseout events).

Try dragin an object in excel, if you reach the end/begining of what you can see it will start scrolling down/up.

I don't know if i explain myself, let me know and i'll try to make it more explicit

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文