WPF - 在拖放期间跟踪鼠标AllowDrop = False 时丢弃 [英] WPF - Track mouse during Drag & Drop while AllowDrop = False
问题描述
我正在尝试创建一个在拖动过程中跟随鼠标的 Adorner &下降操作.即使鼠标拖动到 AllowDrop 设置为 False 的元素上,它也需要这样做.
I'm trying to create an Adorner that will follow the mouse during a drag & drop operation. It needs to do so even when the mouse is dragging over an element that has AllowDrop set to False.
问题:
- 在 Drag & 期间不会触发普通鼠标事件(例如 MouseMove)放下
- 拖动 &放置事件(例如 DragOver、GiveFeedback)仅在有效放置目标(AllowDrop 设置为 true 的元素)上触发
我需要跟踪:
- 鼠标在哪里
- 鼠标移动时
如果没有上述任何事件,我找不到简单的方法来做到这一点.
Without any of the above events, there's no easy way I can find to do this.
我已经通过使用本机 GetCursorPos 方法解决了 #1.这可以随时可靠地获取鼠标的位置.
I've solved #1 by using the native GetCursorPos method. This can reliably get the position of the mouse whenever I want.
我剩下的问题是鼠标移动时收到通知.有什么方法可以在拖动过程中获得鼠标移动通知吗?拖放操作,即使在 AllowDrop 设置为 false 的元素上拖动?
My remaining problem is getting notified when the mouse moves. Is there any way I can get mouse movement notifications during a drag & drop operation, even when dragging over elements with AllowDrop set to false?
注意:我不想使用计时器而只是不断刷新位置(如果可以的话),我更愿意使用实际的鼠标输入.
Note: I don't want to use a timer and just continuously refresh the position (if I can help it), I'd much rather use actual mouse input.
推荐答案
哇,没想到这个这么难.
Wow, I didn't expect this one to be so tough.
我的第一次尝试是尝试绕过 WPF 并直接进入本机窗口消息泵.但事实证明,即使是标准的 WM_MOUSEMOVE 消息也不会在拖放操作期间通过.深入挖掘(通过 ole2.dll 源代码),我发现在拖动过程中创建了一个单独的、不可见的窗口,它吃掉了所有正常的消息,而是直接与放置目标交互(这可能是正常 WPF 鼠标事件的原因一开始就不要开火).
My first attempt was to try and bypass WPF and go straight to the native window message pump. But it turns out that even the standard WM_MOUSEMOVE message doesn't come through during a drag and drop operation. Digging deeper (through the ole2.dll source code), I discovered that a separate, invisible window gets created during the drag which eats up all the normal messages and instead interfaces with the drop targets directly (which is probably why the normal WPF mouse events don't fire in the first place).
我担心这可能会结束,直到我发现 钩子,它可以让您在消息被活动窗口消费之前获取它们.使用 WH_MOUSE 钩子,我能够拦截 WM_MOUSEMOVE 消息并相应地放置我的 Adorner.
I was worried that might be the end of it, until I discovered hooks, which let you get a hold of messages before they're consumed by the active window. Using the WH_MOUSE hook I was able to intercept the WM_MOUSEMOVE message and place my Adorner accordingly.
我不会在这里发布 Adorner 的所有代码,但我会给你我用来跟踪鼠标的 P/Invoke 代码:
I'm not going to post all the code for the Adorner here, but I will give you the P/Invoke code I used to track the mouse:
Module NativeMethods
<DllImport("user32.dll")>
Public Function SetWindowsHookEx(ByVal idHook As HookType, ByVal lpfn As [Delegate], ByVal hInstance As IntPtr, ByVal threadId As Integer) As IntPtr
End Function
<DllImport("user32.dll")>
Public Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")>
Public Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As Boolean
End Function
<StructLayout(LayoutKind.Sequential)>
Friend Structure Win32Point
Public X As Int32
Public Y As Int32
Public Shared Widening Operator CType(Point As Win32Point) As Drawing.Point
Return New Drawing.Point(Point.X, Point.Y)
End Operator
Public Shared Widening Operator CType(Point As Win32Point) As Windows.Point
Return New Windows.Point(Point.X, Point.Y)
End Operator
End Structure
Const WM_MOUSEMOVE As Integer = 512
Enum HookType As Integer
WH_JOURNALRECORD = 0
WH_JOURNALPLAYBACK = 1
WH_KEYBOARD = 2
WH_GETMESSAGE = 3
WH_CALLWNDPROC = 4
WH_CBT = 5
WH_SYSMSGFILTER = 6
WH_MOUSE = 7
WH_HARDWARE = 8
WH_DEBUG = 9
WH_SHELL = 10
WH_FOREGROUNDIDLE = 11
WH_CALLWNDPROCRET = 12
WH_KEYBOARD_LL = 13
WH_MOUSE_LL = 14
End Enum
Public Delegate Function HookProc(ByVal code As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
<StructLayout(LayoutKind.Sequential)>
Structure MOUSEHOOKSTRUCT
Public pt As Win32Point
Public hwnd As IntPtr
Public wHitTestCode As UInteger
Public dwExtraInfo As IntPtr
End Structure
End Module
Class MouseTracker
Private HookHandle As IntPtr
Private HookDelegate As New HookProc(AddressOf NativeHook)
Private Sub AddNativeHook()
#Disable Warning BC40000 ' Type or member is obsolete
HookHandle = SetWindowsHookEx(HookType.WH_MOUSE, HookDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId())
#Enable Warning BC40000 ' Type or member is obsolete
End Sub
Private Sub RemoveNativeHook()
UnhookWindowsHookEx(HookHandle)
End Sub
Private Function NativeHook(code As Integer, wParam As IntPtr, lParam As IntPtr) As Integer
If code >= 0 Then
If wParam = WM_MOUSEMOVE Then
Dim data = Marshal.PtrToStructure(Of MOUSEHOOKSTRUCT)(lParam)
'From here you can use Visual.PointFromScreen(data.pt) to get the coordinates of the mouse relative to any WPF Visual.
'Then you do whatever you want with that!
End If
End If
Return CallNextHookEx(IntPtr.Zero, code, wParam, lParam)
End Function
End Class
如果您需要更多信息,我大量引用:
pinvoke.net:https://pinvoke.net/default.aspx/user32/SetWindowsHookEx.html
关于钩子的 Microsoft 文档:https://docs.microsoft.com/zh-CN/windows/win32/winmsg/about-hooks
If you need more information, I heavily referenced:
pinvoke.net: https://pinvoke.net/default.aspx/user32/SetWindowsHookEx.html
Microsoft docs on hooks: https://docs.microsoft.com/en-us/windows/win32/winmsg/about-hooks
这篇关于WPF - 在拖放期间跟踪鼠标AllowDrop = False 时丢弃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!