问题
I am using the windowsformshost and trying to get dragover and drop events.
I have set allowdrop on the WPF window, Windowsformshost and the ChromiumWebBrowser. I can understand that the WPF window will not get the event due to the windowsformshost airspace issues. But I dont understand why the windowsformshost or the ChromiumWebBrowser does not get any of the events. It appears they are swallowed and not passed on by CEF/CefSharp. How can I handle the events and/or what do I need to disable in CEF/CefSharp?
As I am moving from pure WPF CefSharp I have implemented a dragenter, dragmove and dragdrop with the WPF ChromiumWebBrowser (not using the IDragHandler).
What I would like to achieve is to show a different drag effect based on where the mouse is dragging and secondly I would like to be able to intercept the drop event to first check the file type where the user drops over a file upload type element.
What is the effect of disabling or calling RevokeDragDrop and which Hwnd (window) should this be called on from a Cefsharp point of view?
回答1:
Answering my own question: it is possible to restore the drag and drop events which have been swallowed upstream.
First I used the IOleDropTarget Interface which looks like this;
<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
Next and while we are on the interfaces let use create the nice icons that windows explorer shows when a file is dragged with this interface.
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
You need to implement the IOleDropTaget interface which will then supply the events of DragEnter, Over, Leave and Drop.
To hook up these events you don't need to know anything about the messageloop or wndproc messages as was hinted at above. What you do need to know is that one of the Chromium windows with class name "Chrome_WidgetWin_0" is registered for drag and drop and this has to be first revoked before you can get the events.
CefSharp examples show how to drill down into the Chromium windows but it is normally to get another window class. In this case I use the following (note I revoke all windows I find along the way in the call back function but it appears that only Chrome_WidgetWin_0 is registered.
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
Once you have this handle and you have revoked it as drop target then you can call RegisterDragDrop passing in the handle and your IOleDropTarget class.
A few of my WinAPI signature look like this
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
An example of the event and how to use the IDropTargetHelper is like this
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
Certainly something that would be good to see in CefSharp for WinForms especially as the control has a whole bunch or useless properties (AllowDrop) and events (Drag and Drop) at the moment which are not implemented.
来源:https://stackoverflow.com/questions/54711569/how-to-receive-drag-move-and-drag-drop-events-using-cefsharp-winforms