【问题标题】:WPF - Track mouse during Drag & Drop while AllowDrop = FalseWPF - 在 AllowDrop = False 时拖放期间跟踪鼠标
【发布时间】:2019-12-15 16:17:54
【问题描述】:

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

问题:

  • 在拖放期间不会触发普通鼠标事件(例如 MouseMove)
  • 拖放事件(例如 DragOver、GiveFeedback)仅在有效放置目标(AllowDrop 设置为 true 的元素)上触发

我需要跟踪:

  • 鼠标在哪里
  • 鼠标移动时

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

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

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

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

【问题讨论】:

    标签: wpf drag-and-drop


    【解决方案1】:

    哇,没想到这个这么强。

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

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

    我不会在这里发布 Adorner 的所有代码,但我会给你我用来跟踪鼠标的 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 docs on hooks:https://docs.microsoft.com/en-us/windows/win32/winmsg/about-hooks

    【讨论】:

    • 感谢 Keith,您在我的一个项目上帮助了我很多。我想改进 WinForms 的 Drag&Drop,不喜欢使用其他 API 导致预览图像卡顿或冻结。如果您有兴趣,就在这里:github.com/awaescher/FluentDragDrop
    猜你喜欢
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多