How to receive drag move and drag drop events using CefSharp winforms

偶尔善良 提交于 2019-12-23 01:42:18

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!