WPF - 在拖放过程中跟踪鼠标,而 AllowDrop = False

WPF - Track mouse during Drag & Drop while AllowDrop = False

我正在尝试创建一个将在拖放操作期间跟随鼠标的装饰器。即使鼠标拖过 AllowDrop 设置为 False 的元素,它也需要这样做。

问题:

我需要跟踪:

没有上述任何事件,我找不到简单的方法来做到这一点。

我已经使用本机 GetCursorPos 方法解决了 #1。这可以随时随地可靠地获取鼠标的位置。

我剩下的问题是在鼠标移动时收到通知。有什么方法可以在拖放操作期间获得鼠标移动通知,即使在 AllowDrop 设置为 false 的元素上拖动时也是如此?

注意:我不想使用计时器,只是不断刷新位置(如果我可以帮助的话),我更愿意使用实际的鼠标输入。

哇哦,没想到这么难。

我的第一次尝试是尝试绕过 WPF 并直接进入本机 window 消息泵。但事实证明,即使是标准的 WM_MOUSEMOVE 消息也不会在拖放操作期间通过。深入挖掘(通过 ole2.dll 源代码),我发现在拖动过程中创建了一个单独的、不可见的 window,它吞噬了所有正常消息,而是直接与放置目标交互(这可能是为什么正常的 WPF 鼠标事件首先不触发)。

我担心这可能会结束,直到我发现 hooks,它可以让您在消息被活动 window 消耗之前获取消息。使用 WH_MOUSE 挂钩,我能够拦截 WM_MOUSEMOVE 消息并相应地放置我的 Adorner。

我不会在这里 post 装饰器的所有代码,但我会给你 P/Invoke 我用来跟踪鼠标的代码:

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/en-us/windows/win32/winmsg/about-hooks