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浏览器控件。这有可能实现吗?
你好,你必须添加对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,因为:
- WPF没有收到鼠标右键打开上下文菜单的事件
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; };