原文:【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方法中有这么一段:

代码

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类型的定义。

代码

[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方法映入眼中:

代码

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

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

代码

[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事件:

代码

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的呢):

代码

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

【WPF】监听WPF的WebBrowser控件弹出新窗口的事件的更多相关文章

  1. WPF中禁止WebBrowser控件打开新窗口

    一.针对纯WPF的WebBrowser控件: <summary> Suppress Script Errors In WPF WebBrowser </summary> pub ...

  2. WPF中不规则窗体与WebBrowser控件的兼容问题解决办法

    原文:WPF中不规则窗体与WebBrowser控件的兼容问题解决办法 引言 这几天受委托开发一个网络电视项目,要求初步先使用内嵌网页形式实现视频播放和选单,以后再考虑将网页中的所有功能整合进桌面程序. ...

  3. VC-基础-WebBrowser控件中弹出新网页窗口

    用webbrowser控件浏览网页时,常弹出新的网页窗口,若不做任何控制的话,会在默认浏览器(一般是IE)中打开,这样就在新的窗口打开了,原程序就很难控制了,且存在webbrowser控件和IE的se ...

  4. Android监听Button和ImageButton控件的点击事件

    一.onClick事件 Button和ImageButton都有一个onClick事件,通过自身的.setOnClickListener(OnClickListener)方法添加点击事件 所有的控件都 ...

  5. VC6.0 MFC中WebBrowser控件禁止新窗口弹出的解决办法

    http://blog.csdn.net/gnorth/article/details/7258293 分类: WebBrowser MFC 禁止新窗口2012-02-14 15:25 1787人阅读 ...

  6. C#调用webbrowser,阻止弹出新HTML页面

    参考资料: 1.C#调用webbrowser,阻止弹出新IE窗口 http://www.cnblogs.com/blindman/p/3819649.html 2.[WPF]监听WPF的WebBrow ...

  7. WPF中不规则窗体与WindowsFormsHost控件的兼容问题完美解决方案

    首先先得瑟一下,有关WPF中不规则窗体与WindowsFormsHost控件不兼容的问题,网上给出的解决方案不能满足所有的情况,是有特定条件的,比如  WPF中不规则窗体与WebBrowser控件的兼 ...

  8. Webbrowser控件判断网页加载完毕的简单方法 (转)

    摘自:http://blog.csdn.net/cometnet/article/details/5261192 一般情况下,当ReadyState属性变成READYSTATE_COMPLETE时,W ...

  9. 屏蔽webbrowser控件右键的一种方法

    原文:屏蔽webbrowser控件右键的一种方法 Option ExplicitPrivate Declare Sub ZeroMemory Lib "KERNEL32" Alia ...

随机推荐

  1. 最简单也最难——如何获取到Android控件的高度

    问题 如何获取一个控件的长和高,相信很多朋友第一眼看见这个问题都会觉得很简单,直接在onCreate里面调用getWidth.getMeasuredWidth不就可以获得了吗,但是,事实上是并没有简单 ...

  2. Permutations【python】

    class Solution: # @param num, a list of integer # @return a list of lists of integers def permute(se ...

  3. BZOJ 1009: [HNOI2008]GT考试( dp + 矩阵快速幂 + kmp )

    写了一个早上...就因为把长度为m的也算进去了... dp(i, j)表示准考证号前i个字符匹配了不吉利数字前j个的方案数. kmp预处理, 然后对于j进行枚举, 对数字0~9也枚举算出f(i, j) ...

  4. asp.net core 使用 Redis 和 Protobuf

    asp.net core 使用 Redis 和 Protobuf 前言 上篇博文介绍了怎么样在 asp.net core 中使用中间件,以及如何自定义中间件.项目中刚好也用到了Redis,所以本篇就介 ...

  5. WPF常用转换

    原文 WPF常用转换 以下是代码中常常用到的一些转换,整理如下,后续再不断完善: 1.string和Color的转换 //string转Color (Color)ColorConverter.Conv ...

  6. 设计模式 - 命令模式(command pattern) 宏命令(macro command) 具体解释

    命令模式(command pattern) 宏命令(macro command) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考: 命名模式(撤销) ...

  7. Ch06 验证

    6.1  服务器端验证 6.1.1  Data Annotations验证 6.1.2  扩展ModelMetadtaProvider 6.2  客户端验证 6.2.1  客户端验证初步 6.2.2  ...

  8. VC 对话框背景颜色、控件颜色

    系统环境:Windows 7软件环境:Visual C++ 2008 SP1本次目的:为对话框设置背景颜色.控件颜色 既然MFC对话框不好开发,那么现在我们来开始美化我们的对话框.为对话框设置背景颜色 ...

  9. 略懂 MySQL字符集

    本文虽说旨在明白.但若略懂亦可.毕竟诸葛孔明如是     只有基于字符的值才有所谓字符集的概念     某些字符集可能需要更多CPU.消费更多的内存和磁盘空间.甚至影响索引使用     这还不包括令人 ...

  10. semaphore实现浏览器的读写原理

    在编程范式中的斯坦福大学的老师说了一个例子:好比世界上就只有一台互联网的服务器,当我们浏览网页的时候,就好比服务器进行了写操作,而浏览器则进行了读操作. 我如果用简单的伪代码c++写出来是这个样子的: ...