如何使用 CefSharp winforms 接收拖动移动和拖放事件
How to receive drag move and drag drop events using CefSharp winforms
我正在使用 windowsformshost 并尝试获取 dragover 和 drop 事件。
我已经在 WPF window、Windowsformshost 和 ChromiumWebBrowser 上设置了 allowdrop。我可以理解,由于 windowsformshost 空域问题,WPF window 将无法获取事件。但我不明白为什么 windowsformshost 或 ChromiumWebBrowser 没有收到任何事件。看来它们被 CEF/CefSharp 吞噬了,并没有传递下去。我如何处理事件 and/or 我需要在 CEF/CefSharp 中禁用什么?
当我从纯 WPF CefSharp 转移时,我已经使用 WPF ChromiumWebBrowser(不使用 IDragHandler)实现了 dragenter、dragmove 和 dragdrop。
我想实现的是根据鼠标拖动的位置显示不同的拖动效果,其次我希望能够拦截拖放事件以首先检查用户拖放的文件类型文件上传类型元素。
什么是 effect of disabling 或调用 RevokeDragDrop,从 Cefsharp 的角度来看应该调用哪个 Hwnd (window)?
回答我自己的问题:可以恢复被上游吞没的拖放事件。
首先我使用了如下所示的 IOleDropTarget 接口;
<ComImport, Guid("00000122-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Interface IOleDropTarget
<PreserveSig>
Function OleDragEnter(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal pDataObj As Object,
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
<PreserveSig>
Function OleDragOver(
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
<PreserveSig>
Function OleDragLeave() As Integer
<PreserveSig>
Function OleDrop(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal pDataObj As Object,
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
End Interface
接下来,当我们在界面上时,让我们创建漂亮的图标,当使用此界面拖动文件时,windows 资源管理器会显示这些图标。
Imports IDataObject_Com = System.Runtime.InteropServices.ComTypes.IDataObject
Imports System.Windows.Interop
Imports System.Runtime.InteropServices
Namespace Browser
<StructLayout(LayoutKind.Sequential)>
Public Structure Win32Point
Public x As Integer
Public y As Integer
End Structure
<ComImport>
<Guid("4657278A-411B-11d2-839A-00C04FD918D0")>
Public Class DragDropHelper
End Class
<ComVisible(True)>
<ComImport>
<Guid("4657278B-411B-11D2-839A-00C04FD918D0")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Interface IDropTargetHelper
Sub DragEnter(
<[In]> ByVal hwndTarget As IntPtr,
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal dataObject As IDataObject_Com,
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub DragLeave()
Sub DragOver(
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub Drop(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal dataObject As IDataObject_Com,
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub Show(
<[In]> ByVal show As Boolean)
End Interface
您需要实现 IOleDropTaget 接口,该接口将提供 DragEnter、Over、Leave 和 Drop 事件。
要连接这些事件,您不需要像上面暗示的那样了解有关 messageloop 或 wndproc 消息的任何信息。您需要知道的是,其中一个 Chromium windows 名称 class 名称 "Chrome_WidgetWin_0" 已注册用于拖放,并且必须先将其撤销才能获得事件。
CefSharp 示例展示了如何深入 Chromium windows,但通常是获取另一个 window class。在这种情况下,我使用以下内容(注意我撤销了我在回调函数中找到的所有 windows,但似乎只有 Chrome_WidgetWin_0 被注册了。
Const Chrome_WidgetWin As String = "Chrome_WidgetWin_0"
Private Function TryFindHandl(ByVal browserHandle As IntPtr, <Out> ByRef chromeWidgetHostHandle As IntPtr) As Boolean
Dim cbXL As New NativeMethodsEx.EnumChildCallback(AddressOf EnumChildProc_Browser)
NativeMethodsEx.EnumChildWindows(browserHandle, cbXL, chromeWidgetHostHandle)
Return chromeWidgetHostHandle <> IntPtr.Zero
End Function
Private Shared Function EnumChildProc_Browser(ByVal hwndChild As Integer, ByRef lParam As Integer) As Boolean
Dim buf As New StringBuilder(128)
NativeMethodsEx.GetClassName(hwndChild, buf, 128)
Dim ret = NativeMethodsEx.RevokeDragDrop(hwndChild)
If ret = NativeMethodsEx.DRAGDROP_E_NOTREGISTERED Then
Debug.Print("")
End If
If buf.ToString = Chrome_WidgetWin Then
lParam = hwndChild
Return False
End If
Return True
End Function
一旦你有了这个句柄并且你已经将它作为放置目标撤销,那么你可以调用 RegisterDragDrop 传递句柄和你的 IOleDropTarget class.
我的几个 WinAPI 签名是这样的
Friend Const DRAGDROP_E_NOTREGISTERED = &H80040100
Friend Const DRAGDROP_E_INVALIDHWND = &H80040102
Friend Const DRAGDROP_E_ALREADYREGISTERED = &H80040101
Friend Const E_OUTOFMEMORY = &H8007000E
Friend Const S_OK = 0
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Friend Shared Function GetClassName(ByVal hWnd As System.IntPtr, ByVal lpClassname As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function
Friend Delegate Function EnumChildCallback(ByVal hwnd As Integer, ByRef lParam As Integer) As Boolean
<DllImport("User32.dll")>
Friend Shared Function EnumChildWindows(ByVal hWndParent As Integer, ByVal lpEnumFunc As EnumChildCallback, ByRef lParam As Integer) As Boolean
End Function
<DllImport("ole32.dll")>
Friend Shared Function RegisterDragDrop(ByVal hwnd As IntPtr, DropTarget As Browser.IOleDropTarget) As IntPtr
End Function
<DllImport("ole32.dll")>
Friend Shared Function RevokeDragDrop(ByVal hwnd As IntPtr) As IntPtr
End Function
事件示例以及如何使用 IDropTargetHelper 如下所示
Public Function OleDragEnter(<[In]> <MarshalAs(UnmanagedType.Interface)> pDataObj As Object, <[In]> <MarshalAs(UnmanagedType.U4)> grfKeyState As Integer, <[In]> <MarshalAs(UnmanagedType.U8)> pt As Long, <[In]> <Out> ByRef pdwEffect As Integer) As Integer Implements IOleDropTarget.OleDragEnter
Dim winPT As Win32Point
winPT.x = CInt(pt And &H7FFFFFFF)
winPT.y = CInt((pt >> 32) And &H7FFFFFFF)
Dim eff As DragDropEffects = DragDropEffects.None
'this is my event I am sending back to the browser class to deal with.
RaiseEvent DBDragEnter(eff, New Point(winPT.x, winPT.y))
'you need to pass in the effect
pdwEffect = CInt(eff)
'this is the helper which shows the nice icon you drag around.
ddHelper.DragEnter(targetHwnd, CType(pDataObj, IDataObject_Com), winPT, CInt(eff))
Return NativeMethodsEx.S_OK
End Function
当然,在 CefSharp for WinForms 中看到一些东西会很好,特别是因为控件有一大堆或无用的属性 (AllowDrop) 和事件 (Drag and Drop) 目前尚未实现。
我正在使用 windowsformshost 并尝试获取 dragover 和 drop 事件。
我已经在 WPF window、Windowsformshost 和 ChromiumWebBrowser 上设置了 allowdrop。我可以理解,由于 windowsformshost 空域问题,WPF window 将无法获取事件。但我不明白为什么 windowsformshost 或 ChromiumWebBrowser 没有收到任何事件。看来它们被 CEF/CefSharp 吞噬了,并没有传递下去。我如何处理事件 and/or 我需要在 CEF/CefSharp 中禁用什么?
当我从纯 WPF CefSharp 转移时,我已经使用 WPF ChromiumWebBrowser(不使用 IDragHandler)实现了 dragenter、dragmove 和 dragdrop。
我想实现的是根据鼠标拖动的位置显示不同的拖动效果,其次我希望能够拦截拖放事件以首先检查用户拖放的文件类型文件上传类型元素。
什么是 effect of disabling 或调用 RevokeDragDrop,从 Cefsharp 的角度来看应该调用哪个 Hwnd (window)?
回答我自己的问题:可以恢复被上游吞没的拖放事件。
首先我使用了如下所示的 IOleDropTarget 接口;
<ComImport, Guid("00000122-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Interface IOleDropTarget
<PreserveSig>
Function OleDragEnter(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal pDataObj As Object,
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
<PreserveSig>
Function OleDragOver(
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
<PreserveSig>
Function OleDragLeave() As Integer
<PreserveSig>
Function OleDrop(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal pDataObj As Object,
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
End Interface
接下来,当我们在界面上时,让我们创建漂亮的图标,当使用此界面拖动文件时,windows 资源管理器会显示这些图标。
Imports IDataObject_Com = System.Runtime.InteropServices.ComTypes.IDataObject
Imports System.Windows.Interop
Imports System.Runtime.InteropServices
Namespace Browser
<StructLayout(LayoutKind.Sequential)>
Public Structure Win32Point
Public x As Integer
Public y As Integer
End Structure
<ComImport>
<Guid("4657278A-411B-11d2-839A-00C04FD918D0")>
Public Class DragDropHelper
End Class
<ComVisible(True)>
<ComImport>
<Guid("4657278B-411B-11D2-839A-00C04FD918D0")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Interface IDropTargetHelper
Sub DragEnter(
<[In]> ByVal hwndTarget As IntPtr,
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal dataObject As IDataObject_Com,
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub DragLeave()
Sub DragOver(
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub Drop(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal dataObject As IDataObject_Com,
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub Show(
<[In]> ByVal show As Boolean)
End Interface
您需要实现 IOleDropTaget 接口,该接口将提供 DragEnter、Over、Leave 和 Drop 事件。
要连接这些事件,您不需要像上面暗示的那样了解有关 messageloop 或 wndproc 消息的任何信息。您需要知道的是,其中一个 Chromium windows 名称 class 名称 "Chrome_WidgetWin_0" 已注册用于拖放,并且必须先将其撤销才能获得事件。
CefSharp 示例展示了如何深入 Chromium windows,但通常是获取另一个 window class。在这种情况下,我使用以下内容(注意我撤销了我在回调函数中找到的所有 windows,但似乎只有 Chrome_WidgetWin_0 被注册了。
Const Chrome_WidgetWin As String = "Chrome_WidgetWin_0"
Private Function TryFindHandl(ByVal browserHandle As IntPtr, <Out> ByRef chromeWidgetHostHandle As IntPtr) As Boolean
Dim cbXL As New NativeMethodsEx.EnumChildCallback(AddressOf EnumChildProc_Browser)
NativeMethodsEx.EnumChildWindows(browserHandle, cbXL, chromeWidgetHostHandle)
Return chromeWidgetHostHandle <> IntPtr.Zero
End Function
Private Shared Function EnumChildProc_Browser(ByVal hwndChild As Integer, ByRef lParam As Integer) As Boolean
Dim buf As New StringBuilder(128)
NativeMethodsEx.GetClassName(hwndChild, buf, 128)
Dim ret = NativeMethodsEx.RevokeDragDrop(hwndChild)
If ret = NativeMethodsEx.DRAGDROP_E_NOTREGISTERED Then
Debug.Print("")
End If
If buf.ToString = Chrome_WidgetWin Then
lParam = hwndChild
Return False
End If
Return True
End Function
一旦你有了这个句柄并且你已经将它作为放置目标撤销,那么你可以调用 RegisterDragDrop 传递句柄和你的 IOleDropTarget class.
我的几个 WinAPI 签名是这样的
Friend Const DRAGDROP_E_NOTREGISTERED = &H80040100
Friend Const DRAGDROP_E_INVALIDHWND = &H80040102
Friend Const DRAGDROP_E_ALREADYREGISTERED = &H80040101
Friend Const E_OUTOFMEMORY = &H8007000E
Friend Const S_OK = 0
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Friend Shared Function GetClassName(ByVal hWnd As System.IntPtr, ByVal lpClassname As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function
Friend Delegate Function EnumChildCallback(ByVal hwnd As Integer, ByRef lParam As Integer) As Boolean
<DllImport("User32.dll")>
Friend Shared Function EnumChildWindows(ByVal hWndParent As Integer, ByVal lpEnumFunc As EnumChildCallback, ByRef lParam As Integer) As Boolean
End Function
<DllImport("ole32.dll")>
Friend Shared Function RegisterDragDrop(ByVal hwnd As IntPtr, DropTarget As Browser.IOleDropTarget) As IntPtr
End Function
<DllImport("ole32.dll")>
Friend Shared Function RevokeDragDrop(ByVal hwnd As IntPtr) As IntPtr
End Function
事件示例以及如何使用 IDropTargetHelper 如下所示
Public Function OleDragEnter(<[In]> <MarshalAs(UnmanagedType.Interface)> pDataObj As Object, <[In]> <MarshalAs(UnmanagedType.U4)> grfKeyState As Integer, <[In]> <MarshalAs(UnmanagedType.U8)> pt As Long, <[In]> <Out> ByRef pdwEffect As Integer) As Integer Implements IOleDropTarget.OleDragEnter
Dim winPT As Win32Point
winPT.x = CInt(pt And &H7FFFFFFF)
winPT.y = CInt((pt >> 32) And &H7FFFFFFF)
Dim eff As DragDropEffects = DragDropEffects.None
'this is my event I am sending back to the browser class to deal with.
RaiseEvent DBDragEnter(eff, New Point(winPT.x, winPT.y))
'you need to pass in the effect
pdwEffect = CInt(eff)
'this is the helper which shows the nice icon you drag around.
ddHelper.DragEnter(targetHwnd, CType(pDataObj, IDataObject_Com), winPT, CInt(eff))
Return NativeMethodsEx.S_OK
End Function
当然,在 CefSharp for WinForms 中看到一些东西会很好,特别是因为控件有一大堆或无用的属性 (AllowDrop) 和事件 (Drag and Drop) 目前尚未实现。