在 WinForms 中,调用 aDoDragDrop
开始拖动项目后,控件不再使用鼠标滚轮滚动,并且MouseWheel
不再调用控件的事件,直到用户放下他正在拖动的任何内容。
有没有办法让鼠标滚轮在拖动时工作?
在 WinForms 中,调用 aDoDragDrop
开始拖动项目后,控件不再使用鼠标滚轮滚动,并且MouseWheel
不再调用控件的事件,直到用户放下他正在拖动的任何内容。
有没有办法让鼠标滚轮在拖动时工作?
你可以得到一个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
,该事件的行为类似于内置 Windows 窗体控件类中的事件。
(此外,代码被拆分为一个抽象类WindowsHooks
以与其他钩子一起使用,一个SafeHookHandle
确保释放句柄的类和本地方法的帮助类)
您应该查看SetWindowsHookEx
并CALLBACK LowLevelMouseProc
了解其背后的技术。
此事件不仅限于您的应用程序,还将捕获表单外的鼠标,因此它也适用于您无法使用本地事件的操作。
不,在 D+D 期间没有可识别的焦点,并且 D+D 事件不报告鼠标滚轮运动。一个典型的技巧是使用 DragOver 并检查拖动光标是否靠近可滚动区域的任一端。并用计时器滚动。一个例子是here。
除了使用内置的 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 黑盒更简单。
这个怎么样:
在目标 DataGrid(您想放置的那个)中,当鼠标指针到达结尾或开头时,您开始向下或向上滚动(当然它将控制 mousein/mouseout 事件)。
尝试在 excel 中拖动一个对象,如果您到达您所看到的内容的结尾/开头,它将开始向下/向上滚动。
我不知道我是否解释自己,让我知道,我会尽量让它更明确