WPF WebBrowser控件的自定义上下文菜单

本文关键字:上下文 菜单 自定义 WebBrowser 控件 WPF | 更新日期: 2023-09-27 18:02:33

嗨,我需要在wpf中创建一个web浏览器控件的自定义上下文菜单。这是我的xaml代码是不工作:

<WebBrowser x:Name="EmailBox"  ap:BrowserBehavior.HtmlString="{Binding Message, Mode=OneWay}">
    <WebBrowser.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Copy" Command="ApplicationCommands.Copy"/>
            <MenuItem Header="Copy to Customer Reference ID" 
                  Command="{Binding CopyID}"
                  CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, 
                  Path=PlacementTarget.Selection.Text}">
                <MenuItem.Icon>
                    <Image Source="{StaticResource CopyImageSource}" Width="16" />
                </MenuItem.Icon>
            </MenuItem>
            <MenuItem Header="Copy to Comments"
                  Command="{Binding CopyToCommentsCommand}"
                  CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, 
                  Path=PlacementTarget.Selection.Text}">
                <MenuItem.Icon>
                    <Image Source="{StaticResource NoteCopyI}" Width="16" />
                </MenuItem.Icon>
            </MenuItem>
        </ContextMenu>
    </WebBrowser.ContextMenu>
</WebBrowser>

我从其他地方复制了上下文菜单代码。这适用于其他控件,但不适用于web浏览器控件。这有可能实现吗?

WPF WebBrowser控件的自定义上下文菜单

你好,你必须添加对Microsoft HTML对象库的引用,然后…

XAML

<Window x:Class="WPFCustomContextMenuInWebBrowser.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFCustomContextMenuInWebBrowser"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Window.Resources>
    <ContextMenu x:Key="MnuCustom" StaysOpen="True">
      <MenuItem Header="Custom 1"></MenuItem>
      <MenuItem Header="Custom 1"></MenuItem>
      <MenuItem Header="Custom 3"></MenuItem>
    </ContextMenu>
  </Window.Resources>
    <Grid>
        <WebBrowser x:Name="Wb"></WebBrowser>
    </Grid>
</Window>
c#

using System.Windows.Controls;
using MSHTML;
namespace WPFCustomContextMenuInWebBrowser {
  public partial class MainWindow {
    private HTMLDocumentEvents2_Event _docEvent;
    public MainWindow() {
      InitializeComponent();
      Wb.Navigate("http://google.com");
      Wb.LoadCompleted += delegate {
        if (_docEvent != null) {
          _docEvent.oncontextmenu -= _docEvent_oncontextmenu;
        }
        if (Wb.Document != null) {
          _docEvent = (HTMLDocumentEvents2_Event)Wb.Document;
          _docEvent.oncontextmenu += _docEvent_oncontextmenu;
        }
      };
    }
    bool _docEvent_oncontextmenu(IHTMLEventObj pEvtObj) {
      WbShowContextMenu();
      return false;
    }
    public void WbShowContextMenu() {
      ContextMenu cm = FindResource("MnuCustom") as ContextMenu;
      if (cm == null) return;
      cm.PlacementTarget = Wb;
      cm.IsOpen = true;
    }
  }
}

不,这是不可能的。
WebBrowser控件是对本地WebBrowser ActiveX组件的一个非常薄的包装,该组件是Internet Explorer子系统的一部分。它托管在自己的窗口主机上(WPF窗口和WebBrowser有不同的hwnd),所以WPF只知道焦点进入和离开WebBrowser,但不知道任何键盘/鼠标事件。此外,还有所谓的"空域问题":wpf渲染和屏幕区域的原生区域不能重叠。
因此,你不能在WebBrowser中使用WPF ContextMenu,因为:

  1. WPF没有收到鼠标右键打开上下文菜单的事件

  2. WPF无法在WebBrowser上方绘制上下文菜单

另外,我不认为在浏览器的内容中有简单的方法可以用html/js模拟ContextMenu -我记得,ActiveX组件使用IE5 (quirk)渲染模式,如果不更改注册表文件,则不可能更改该模式。
您可以尝试在WebBrowser中使用ActiveX API。文档对象禁用本地上下文菜单,并通过WinAPI自己绘制另一个上下文菜单,这不是一件容易的事。因此,我建议寻找其他纯wpf浏览器控件或HTML渲染器,例如awesomium

XAML如下:

<!--WebBrowser to Display Chat Messages-->
    <WebBrowser Name="webBrowser"
                    Source="http://stakoverflow.com"                        
                    Navigated="webBrowser_Navigated"
                    Navigating="webBrowser_Navigating"
                    LoadCompleted="webBrowser_LoadCompleted">
        <WebBrowser.ContextMenu>
            <ContextMenu x:Name="wbContextMenu" >
                <MenuItem x:Name="menuItem1" Header="Test Item" Click="menuItem1_Click" />
            </ContextMenu>
        </WebBrowser.ContextMenu            
    </WebBrowser>

代码如下:

using mshtml;
private mshtml.HTMLDocumentEvents2_Event documentEvents;

在构造函数或xaml设置你的LoadComplete事件:

webBrowser.LoadCompleted += webBrowser_LoadCompleted;

然后在该方法中创建新的web浏览器文档对象,并查看可用的属性和创建新的事件,如下所示:

private void webBrowser_LoadCompleted(object sender, NavigationEventArgs e)
{
    documentEvents = (HTMLDocumentEvents2_Event)webBrowserChat.Document; // this will access the events properties as needed
    documentEvents.oncontextmenu += webBrowserChat_ContextMenuOpening;
}
private bool webBrowserChat_ContextMenuOpening(IHTMLEventObj pEvtObj)
{
    wbContextMenu.PlacementTarget = pEvtObj as ContextMenu; // Creates target spot where contextmenu will appear
    wbContextMenu.IsOpen = true; // Opens contextmneu
    return false; // ContextMenu wont open
    // return true;  ContextMenu will open
    // Here you can create your custom contextmenu or whatever you want
}

我已经采取了@MartinHoly提供的解决方案,我遇到了这个问题:上下文菜单只能弹出一次(如果你右键单击滚动条,例如,或者选择一个自定义菜单项-下次你右键单击WebBrowser带来标准IE菜单)。我做了以下工作:xaml:

<Window x:Class="WPFCustomContextMenuInWebBrowser.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WPFCustomContextMenuInWebBrowser"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <ContextMenu x:Key="MnuCustom" StaysOpen="True">
      <MenuItem Header="Custom 1"></MenuItem>
      <MenuItem Header="Custom 2"></MenuItem>
      <MenuItem Header="Custom 3"></MenuItem>
    </ContextMenu>
  </Window.Resources>
<Grid>
    <WebBrowser x:Name="Browser"></WebBrowser>
</Grid>

后面的代码:

using System.Windows.Controls;
using MSHTML;
namespace WPFCustomContextMenuInWebBrowser {
  public partial class MainWindow {
    public MainWindow() 
    {
      InitializeComponent();
      Browser.LoadCompleted += BrowserOnLoadCompleted;
    }
    void BrowserOnLoadCompleted(object sender, NavigationEventArgs navigationEventArgs)
    {
        var mshtmlDoc = Browser.Document as HTMLDocument;
        if (mshtmlDoc == null) return;
                    var doc2event = mshtmlDoc as HTMLDocumentEvents2_Event;
        if (doc2event != null)
        {               
            doc2event.onfocusin += FocusInContextMenu;
        }            
    }
    bool OpenContextMenu(IHTMLEventObj pEvtObj)
    {
        WbShowContextMenu(pEvtObj as ContextMenu);
        return false;
    }
    void FocusInContextMenu(IHTMLEventObj pevtobj)
    {
        var mshtmlDoc = Browser.Document as HTMLDocument;
        var doc2event = mshtmlDoc as HTMLDocumentEvents2_Event;
        if (doc2event != null)
        {
            doc2event.oncontextmenu -= OpenContextMenu;
            doc2event.onfocusin -= FocusInContextMenu;
            doc2event.oncontextmenu += OpenContextMenu;
            doc2event.onfocusin += FocusInContextMenu;
        }
    }
    public void WbShowContextMenu() 
    {
      ContextMenu cm = FindResource("MnuCustom") as ContextMenu;
      if (cm == null) return;
      cm.PlacementTarget = Browser;
      cm.IsOpen = true;
    }
  }
}

我有一个间接实现,它涉及到在c#和javascript中相互调用。

xaml:

    <WebBrowser x:Name="webbrowser">
        <WebBrowser.ContextMenu>
            <ContextMenu>
                <MenuItem Header="item1"/>
            </ContextMenu>
        </WebBrowser.ContextMenu>
    </WebBrowser>
c#:

    using System.Runtime.InteropServices;

    public MainWindow()
    {
        InitializeComponent();
        //webbrowser.Navigate(new Uri("https://www.google.com"));
        webbrowser.ObjectForScripting = new ScriptManager(this);
        webbrowser.LoadCompleted += Webbrowser_LoadCompleted;
    }
    private void Webbrowser_LoadCompleted(object sender, NavigationEventArgs e)
    {
        //call C# method and disable the default contextmenu here.
        webbrowser.InvokeScript("eval", new object[] { "document.oncontextmenu = function() { window.external.ShowContextMenu(); return false; };" });
    }
    [ComVisible(true)]
    public class ScriptManager
    {
        private MainWindow mainWindow;
        public ScriptManager(MainWindow MainWindow)
        {
            mainWindow = MainWindow;
        }
        public void ShowContextMenu()
        {
            mainWindow.webbrowser.ContextMenu.IsOpen = true;
        }
    }

但是当页面加载时,它仍然有一个本地上下文菜单,因为LoadCompleted事件没有触发。

因此,如果html页面是由self编写的,您可以直接将这行添加到html中的脚本部分,而不需要LoadCompleted事件:

document.oncontextmenu = function() { window.external.ShowContextMenu(); return false; };