问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何监听WPF的WebBrowser控件弹出新窗口的事件

发布网友 发布时间:2022-04-23 16:04

我来回答

1个回答

热心网友 时间:2022-04-12 23:57

如果使用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中打开:
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
手机导航用电脑软件哪个好导航软件哪个最准确 我要地图网使用我要地图网轻松查询地图信息 装配行业MES系统四大车间管理活动介绍 王者荣耀S14狂铁怎么玩_王者荣耀S14狂铁出装铭文推荐 沅陵县有姓文的分布在那个地方, 汨罗一中在省排名 汨罗高中有哪些? 湖南省岳阳市汨罗有哪些高中 新生儿脊膜膨出后遗症 在深圳硬脊膜膨出手术后大小便失禁怎么办? WPF的WebBrowser如何获取跳转后的页面源码? wpf webbrowser 内容不显示 WPF对webbrowser里的网页进行截图 WPF中WebBrowser如何让跳出去的网页放到一个新的窗体中 WPF中WebBrowser打开PPT wpf中怎么用WebBrowser显示项目中的图片 有喜欢听墨明淇妙的歌吗? 钟恩淇的主要作品 赵梓淇的全部资料 赵梓淇的介绍 爸爸祝福女儿赵梓淇生日快乐的藏头诗怎么写呀忙个忙 谢谢 史记,春秋,汉书,晋书,各是哪个朝代的书 汉书与史记在写司马相如上有何异同 司马迁,司马懿,司马光有什么关系 司马光,司马迁,司马懿,司马相如他们几个到底什么关系??? 两司马的生平 司马氏的资料 司马迁是东汉还是西汉人 司马迁是怎样写成史记的 《史记》两汉属于半禁书状态,为何东汉中期才有完整版? WPF的WebBrowser问题(好的我感激不尽,加加加分) 关于wpf WebBrowser传递参数的问题 WPF中使用Winform中的WebBrowser控件,背景透明时,WebBrowser消失 WPF下如何去除webbrowser的滚动条? 为什么WPF webbrowser 里的网页内容点击无法链接解决办法 怎样在WPF中添加webbrowser控件并能调整它的大小,同时在窗体中加上一个richtextbox …… WPF中webBrowser控件如何显示隐藏网页部分内容 wpf webBrowser 禁用超链接 WPF里的webBrowser为什么只有Document属性,没有DocumentText属性? WPF中使用WebBrowser控件,怎么自定义它的滚动条样式 海带能和土豆一起炖汤吃吗? 土豆海带一起煮的危害 海带和土豆可以一起炖吗? 土豆能和海带同食吗? 土豆,胡萝卜,海带可以一块吃吗? 海带和土豆能同时吃吗? 海带和土豆可以共食吗? 土豆海带可以一起煮吗? 土豆海带花生能同时吃吗 怎么查看微信删除的账单记录