【问题标题】:Custom context menu for WPF WebBrowser ControlWPF WebBrowser 控件的自定义上下文菜单
【发布时间】:2015-07-14 15:44:04
【问题描述】:

您好,我需要为 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>

我从其他地方复制了上下文菜单代码。这适用于其他控件,但不适用于 webbrowser 控件。是否有可能完成这项工作?

【问题讨论】:

    标签: c# wpf visual-studio-2010


    【解决方案1】:

    您好,您必须添加对 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;
        }
      }
    }
    

    【讨论】:

    • 您好,感谢您的回答。我最终通过包含浏览器控件和 xaml 控件解决了这个问题。我们允许用户在浏览器控件和 xaml 控件之间来回切换。用户现在似乎喜欢这个解决方案。
    • 解决方案的问题是一次工作-例如,如果您右键单击滚动条,或者选择自定义菜单项-下次您右键单击WebBrowser会带来标准的IE菜单... :-(
    【解决方案2】:

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

    1. WPF 不接收鼠标右键单击事件以打开上下文菜单
    2. WPF 无法在 WebBrowser 上方绘制上下文菜单

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

    【讨论】:

      【解决方案3】:

      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;
      

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

      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
      }
      

      【讨论】:

      • 解决方案的问题是一次工作 - 例如,如果您右键单击滚动条,或者选择自定义菜单项 - 下次您右键单击 WebBrowser 会带来标准 IE菜单... :-(
      • 我不确定您为什么会遇到这个问题,您应该确保您返回 true 以显示您自己的自定义上下文菜单。没有理由它会显示默认的 IE 菜单....分享您的代码,也许我可以为您指明正确的方向....
      • 我使用了@MartinHoly 的代码...我已在此处添加了如何解决此问题的答案 - 请查看。没有时间和承诺去找出它为什么会这样……
      【解决方案4】:

      我采用了@MartinHoly 提供的解决方案,但遇到了问题:上下文菜单只能弹出一次(例如,如果您右键单击滚动条,或选择自定义菜单项 -下次你右击 WebBrowser 时会带来标准的 IE 菜单)。我做了以下解决方法: xml:

      <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;
          }
        }
      }
      

      【讨论】:

        【解决方案5】:

        我有一个间接实现,其中涉及在 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页面是自己写的,可以直接在html中的script部分添加这一行,不需要LoadCompleted事件:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          • 1970-01-01
          相关资源
          最近更新 更多