【问题标题】:Show Winforms Contextmenu over ChromiumWebBrowser在 ChromiumWebBrowser 上显示 Winforms 上下文菜单
【发布时间】:2016-02-16 03:00:23
【问题描述】:

我正在开发一个使用 CefSharp 作为 Gui 的 Winforms 项目。出于几个原因,我想使用 Winforms ContextMenu 类实现自定义上下文菜单;以 Html 呈现菜单或自定义 ChromiumWebBrowser 的上下文菜单(使用 CefSharp.IContextMenuHandler)不是一个选项。

它由 Javascript 代码触发的上下文菜单,该代码调用我传递给 RegisterAsyncJsObject 的 .net 对象上的方法;使用 Javascript 会阻止默认上下文菜单。我在 Gui 线程上调用方法调用,因为通过“javascript 桥”对注册对象的调用来自不同的线程。

我的问题:在 CefSharp.WinForms.ChromiumWebBrowser 上手动显示 Winforms 上下文菜单时,上下文菜单无法获得键盘焦点(例如,使用箭头键选择项目不起作用,也无法使用 Esc 关闭上下文菜单) ;相反,键盘焦点仍保留在 ChromiumWebBrowser 控件上。而且,如果我单击 ChromiumWebBrowser 的控制区域,上下文菜单也不会关闭。我只能通过用鼠标选择一个项目或单击表单中的另一个控件(其中包含 ChromiumWebBrowser)或完全其他地方(例如桌面或其他应用程序)来关闭上下文菜单。

如果我从代码中的其他地方触发上下文菜单 - 最终使用调用 myContextMenu.Show() 的相同方法 - 上下文菜单会根据需要获得键盘焦点。但是仍然存在一个问题:当我在 ChromiumWebBrowser 控件中单击时它没有关闭。

我没有使用过 IFocusHander、IContextMenuHandler、IKeyboardHandler - 应该吗?

我正在使用 CEF 3.2454.1344.g2782fb8、Chromium 45.0.2454.101 和 .net 4.5.1。

不幸的是,提取演示代码是不可能的。

有人有什么想法吗?

编辑1: 看完了cmets,我决定把代码流程描述的更准确些:

  • 当右键单击 Javascript 时,会向注册的 .net 对象发送一条消息,其中包含鼠标坐标。通过在 ContextMenu 事件的 MouseEvent 参数上设置 preventDefault 来阻止默认上下文菜单。
  • 已注册的 .net 对象接收消息并调用 windowForm.Invoke(Sub() ... ),因为消息未在 Main/Gui 线程上接收,但必须在那里处理才能正确显示上下文菜单。
  • 创建上下文菜单并将其分配给包含实际 ChromiumWebBrowser 控件的 UserControl 的 ContextMenuStrip 属性。
  • 使用ContextMenuStrip.Show(location)方法显示。

问题:

  • 上下文菜单没有键盘焦点。
  • ChromiumWebBrowser 似乎“吞噬”了所有鼠标事件:单击此处不会关闭上下文菜单。
  • 除了使用不同的“触发器”之外,以相同方式打开上下文菜单可以正常工作,但第 2 期除外。

【问题讨论】:

  • 首先,默认是在它自己的线程中运行消息循环,这在某些情况下会导致问题,比如与菜单交互。还有另一种选择,请参阅github.com/cefsharp/CefSharp/commit/… 了解一些粗略的细节。从那开始,然后报告。我个人会考虑通过IContextMenuHandler修改现有的上下文菜单,添加、删除命令。
  • 作为参考,可能需要提供一个工作示例,您的问题很复杂,并且在您的工作方面有点超出常规。 MinimalExample 项目可用作参考点。 github.com/cefsharp/CefSharp.MinimalExample 显然从改变消息循环开始,看看你能走多远。
  • @amaitland:感谢您的反馈!使用IContextMenuHandler 不是一个可行的解决方案,因为上下文菜单使用了几个自定义菜单项类(在应用程序仅 Gdi 时实现),而且浏览器的上下文菜单看起来也不同:我正在寻求无缝集成,即用户不应该注意到 Gui 实际上是 Html。
  • @amaitland#2:我尝试设置MultiThreadedMessageLoop = False,但屏幕只是保持黑色。这可能是“设计使然”,但我不明白的设计。我读了一些关于CefDoMessageLoopWork 等的内容,但无法真正理解它。另外,因为我觉得默认设置可能更有意义。或许您可以简单解释一下如何处理替代设置?
  • @amaitland#3:我仍然看不到自己一起获得一个工作示例,但在编辑中更详细地描述了代码流;也许这会有所帮助。

标签: winforms cefsharp


【解决方案1】:

最终解决方案很简单;如果添加了以下步骤,一切都会按预期进行:

在显示上下文菜单之前,使用 ChromiumWebBrowser 禁用 UserControl 并将焦点设置为拥有的表单;像这样:

Private Sub showContextMenu(position As Point)
    Me.ctrlCefBrowser.Enabled = False
    Me.Focus()

    myContextMenu.Show(position)
End Sub

这将焦点从 ChromiumWebBrowser 移开,让上下文菜单有机会响应键盘输入。而且,通过禁用控件,鼠标事件不再被“吞噬”,因此单击浏览器区域会导致上下文菜单再次消失。

然后,最后,将事件处理程序添加到上下文菜单以再次重新启用浏览器控件:

Private Sub myContextMenu_Closed(sender As Object, e As ToolStripDropDownClosedEventArgs) Handles myContextMenu.Closed
    Me.ctrlCefBrowser.Enabled = True
    Me.ctrlCefBrowser.Focus()
End Sub

这对我有用,现在我的网络浏览器控件有一个完全可定制的 Gdi 上下文菜单 :o)

注意: 使用其他菜单时也会出现类似的问题,例如在主菜单或工具栏中:单击 ChromiumWebBrowser 控件不会关闭菜单(因为鼠标事件也被“吞下”)。可以应用相同的解决方案:打开下拉菜单时停用 (Enabled = False) 网络浏览器控件。当它关闭时,重新激活它。对于我的菜单,我使用了一个派生类 (Inherits ToolStripMenuItem),它将侦听器添加到相应的事件中。这以一种全局且简单的方式解决了问题。

编辑: 上面提出的解决方案留下了点击禁用的浏览器控件按预期关闭菜单但丢失的问题,即浏览器无法处理它。我现在的解决方法是:

  • 不要禁用浏览器控件。
  • 使用菜单项和上下文菜单的打开事件,跟踪当前打开的菜单。
  • 当浏览器接收到焦点(可通过拦截 WndProc 消息获得)时,关闭打开的菜单。

实施实际的解决方案在细节上引起了一些麻烦,但无论如何这可能对某人有所帮助...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多