【WPF】监听WPF的WebBrowser控件弹出新窗口的事件

简介: 原文:【WPF】监听WPF的WebBrowser控件弹出新窗口的事件WPF中自带一个WebBrowser控件,当我们使用它打开一个网页,例如百度,然后点击它其中的链接时,如果这个链接是会弹出一个新窗口的,那么它会生生的弹出一个IE窗口来,而不是在内部跳到该链接。
原文: 【WPF】监听WPF的WebBrowser控件弹出新窗口的事件

WPF中自带一个WebBrowser控件,当我们使用它打开一个网页,例如百度,然后点击它其中的链接时,如果这个链接是会弹出一个新窗口的,那么它会生生的弹出一个IE窗口来,而不是在内部跳到该链接。

如果使用Winform的WebBrowser控件,我们可以监听它的NewWindow事件,在这个事件中做一些处理,例如,在新建一个Tab来打开,或者控制它在当前WebBrowser中跳转。很不幸的是,WPF的WebBrowser没有这个事件。

说到底,Winform的WB或者是WPF的WB都是在调用IE的一个控件,因此,Winform能加上的,我们WPF一定也有办法加上。如此,那我们就请出神器Reflector,研究一把。

首先,我们打开Winform的WebBrowser,找到触发NewWindow事件的代码:

     protected   virtual   void  OnNewWindow(CancelEventArgs e)
    {
        
if  ( this .NewWindow  !=   null )
        {
            
this .NewWindow( this , e);
        }
    }

它是在OnNewWindow方法中触发的。那么,是谁调用了这个OnNewWindow呢?接着搜索,最后在一个叫WebBrowserEvent的类里面发现这么一段:

public   void  NewWindow2( ref   object  ppDisp,  ref   bool  cancel)
{
     CancelEventArgs e 
=   new  CancelEventArgs();
     
this .parent.OnNewWindow(e);
     cancel 
=  e.Cancel;
}

我们接着搜NewWindow2,却发现没有地方显式地调用它了。既然从方法入手没找到,那我们就来研究一下定义这个方法的WebBrowserEvent,看看是谁在使用它。
仔细搜索一遍,最后发现在WebBrowser的CreateSink方法中有这么一段:

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
protected   override   void  CreateSink()
{
    
object  activeXInstance  =   base .activeXInstance;
    
if  (activeXInstance  !=   null )
    {
        
this .webBrowserEvent  =   new  WebBrowserEvent( this );
        
this .webBrowserEvent.AllowNavigation  =   this .AllowNavigation;
        
this .cookie  =   new  AxHost.ConnectionPointCookie(activeXInstance,  this .webBrowserEvent,  typeof (UnsafeNativeMethods.DWebBrowserEvents2));
    }
}

注意这句话:

this.cookie = new AxHost.ConnectionPointCookie(activeXInstance, this.webBrowserEvent, typeof(UnsafeNativeMethods.DWebBrowserEvents2));

很显然,这句话是关键。AxHost.ConnectionPointCookie类的作用是:“将一个ActiveX 控件连接到处理该控件的事件的客户端”。

上面的调用中有一个很奇怪的类型:DWebBrowserEvents2,熟悉COM的童鞋应该马上能想到,这其实是一个COM类型的定义。

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
[ComImport, TypeLibType(TypeLibTypeFlags.FHidden), InterfaceType(ComInterfaceType.InterfaceIsIDispatch), Guid( " 34A715A0-6587-11D0-924A-0020AFC7AC4D " )]
public   interface  DWebBrowserEvents2
{
     ......
}

实际上,我们再去看WebBrowserEvent的定义,它恰恰是实现了这个接口的。

[ClassInterface(ClassInterfaceType.None)]
private   class  WebBrowserEvent : StandardOleMarshalObject, UnsafeNativeMethods.DWebBrowserEvents2
{
    ......
}

因此,上面这句话不难理解,就是定义一个实现了特定COM接口的类型,让浏览器控件的事件能够转发到这个类型实例去处理。因此,NewWindow2其实是浏览器控件去调用的。

Winform的WebBrowser我们搞清楚了,下面我们来看WPF的。其实,打开WPF的WebBrowser代码之后,我们会发现它跟Winform的WebBrowser机制是一样的。一个似曾相识的CreateSink方法映入眼中:

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
[SecurityTreatAsSafe, SecurityCritical]
internal   override   void  CreateSink()
{
    
this ._cookie  =   new  ConnectionPointCookie( this ._axIWebBrowser2,  this ._hostingAdaptor.CreateEventSink(),  typeof (UnsafeNativeMethods.DWebBrowserEvents2));
}

这儿也有一个ConnectionPointCookie,但是它的访问权限是internal的:(
第二个参数,_hostingAdapter.CreateEventSink返回的是什么呢:

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
[SecurityCritical]
internal   virtual   object  CreateEventSink()
{
    
return   new  WebBrowserEvent( this ._webBrowser);
}

[ClassInterface(ClassInterfaceType.None)]
internal   class  WebBrowserEvent : InternalDispatchObject < UnsafeNativeMethods.DWebBrowserEvents2 > , UnsafeNativeMethods.DWebBrowserEvents2
{
    ......
}

仍然是一个WebBrowserEvent!悲剧的是,这个WPF的WebBrowserEvent,并没有触发NewWindowEvent:

public   void  NewWindow2( ref   object  ppDisp,  ref   bool  cancel)
{
}

现在知道为什么WPF的WB控件没有NewWindow事件了吧?微软的童鞋压根儿就没写!

既然微软的童鞋不写,那我们就自己折腾一把,反正原理已经搞清楚了。

首先,我们也得定义一个DWebBrowserEvents2接口,这个我们直接通过Reflector复制一份就好了。代码就不贴上来了。

接着,我们再仿造一个WebBrowserEvent,关键是要触发NewWindow事件:

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
public   partial   class  WebBrowserHelper
    {
        
private   class  WebBrowserEvent : StandardOleMarshalObject, DWebBrowserEvents2
        {
            
private  WebBrowserHelper _helperInstance  =   null ;

            
public  WebBrowserEvent(WebBrowserHelper helperInstance)
            {
                _helperInstance  =  helperInstance;
            }
            ......
            
            
public   void  NewWindow2( ref   object  pDisp,  ref   bool  cancel)
            {
                _helperInstance.OnNewWindow( ref  cancel);
            }
            ......
        }
    }

 

最后,我们需要仿造Framework中的代码,也来CreateSink一把(我承认,用了反射来取WebBrowser内部的东东,谁让这些类型都是internal的呢):

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
private   void  Attach()
{
    var axIWebBrowser2  =  _webBrowser.ReflectGetProperty( " AxIWebBrowser2 " );
    var webBrowserEvent  =   new  WebBrowserEvent( this );
    var cookieType  =   typeof (WebBrowser).Assembly.GetType( " MS.Internal.Controls.ConnectionPointCookie " );
    _cookie  =  Activator.CreateInstance(
        cookieType,
        ReflectionService.BindingFlags,
        
null ,
        
new [] { axIWebBrowser2, webBrowserEvent,  typeof (DWebBrowserEvents2) },
        CultureInfo.CurrentUICulture);
}


最后的使用:

var webBrowserHelper  =   new  WebBrowserHelper(webBrowser);
......
webBrowserHelper.NewWindow 
+=  WebBrowserOnNewWindow;


【效果图】

初始网页:


点击一个链接,默认情况下,将是弹出一个IE窗口,现在是在新的Tab中打开:

 

【示例代码】

(新建按钮点击后,请输入完整的网址,例如:http://www.sina.com)

/Files/RMay/WpfWebBrowser.zip

 



目录
相关文章
|
16天前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
|
4月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
65 1
|
8月前
|
C# Windows
WPF技术之RichTextBox控件
WPF RichTextBox是Windows Presentation Foundation (WPF)中提供的一个强大的文本编辑控件,它可以显示富文本格式的文本,支持多种文本处理操作。
346 0
|
4月前
|
前端开发 C# 容器
浅谈WPF之控件拖拽与拖动
使用过office的visio软件画图的小伙伴都知道,画图软件分为两部分,左侧图形库,存放各种图标,右侧是一个画布,将左侧图形库的图标控件拖拽到右侧画布,就会生成一个新的控件,并且可以自由拖动。那如何在WPF程序中,实现类似的功能呢?今天就以一个简单的小例子,简述如何在WPF中实现控件的拖拽和拖动,仅供学习分享使用,如有不足之处,还请指正。
108 2
|
17天前
|
C# 开发者 C++
一套开源、强大且美观的WPF UI控件库
一套开源、强大且美观的WPF UI控件库
130 0
|
5月前
|
算法 C# UED
浅谈WPF之控件模板和数据模板
WPF不仅支持传统的Windows Forms编程的用户界面和用户体验设计,同时还推出了以模板为核心的新一代设计理念。在WPF中,通过引入模板,将数据和算法的“内容”和“形式”进行解耦。模板主要分为两大类:数据模板【Data Template】和控件模板【Control Template】。
96 8
|
8月前
|
定位技术 C# UED
WPF技术之ScrollViewer控件
WPF ScrollViewer是WPF中常用的一个控件,它提供了滚动视图的功能,可用于显示超出容器可视区域的内容。ScrollViewer通常用于容纳大量内容的控件,以在有限的空间内显示这些内容,并允许用户通过滚动来查看隐藏的部分。
708 0
|
8月前
|
前端开发 C#
WPF技术之ContentControl 控件
ContentControl 是 WPF 中的一个常见控件,用于显示单个内容元素。它可以包含任意类型的内容,包括文本、图像、控件等。
776 0
|
8月前
|
XML C# 数据格式
WPF技术之DocumentViewer控件
WPF 的 DocumentViewer 是一个强大的控件,用于在应用程序中显示各种类型的文档,如 XPS(XML Paper Specification)、FlowDocument 和 FixedDocument 等。
1077 1
|
8月前
|
搜索推荐 C# Windows
WPF技术之MediaElement控件
WPF(Windows Presentation Foundation)MediaElement是一种用于创建用户界面的框架,它提供了丰富的图形、多媒体和动画功能。它可以播放各种类型的音频和视频文件,包括本地文件和网络流。
301 0