强制所有网页链接在同一页面打开或者在TabControl中弹出新窗口
IEwebbrowser中老生常谈的话题。
一般的解决都是通过
// webBrowser.Navigating += WebBrowser_Navigating; 注册转跳前事件
private void WebBrowser_Navigating(object sender, System.Windows.Forms.WebBrowserNavigatingEventArgs e)
{
webBrowser.Navigate("新的网页地址");
}
但是并不是特别的好用,比如网页中设置是弹出窗口来跳转网页
下面我会将为什么不好使用,已经正确的用法
好在是C# 4.72开源了。不用反编译了。 有些东西也好解释了开源地址
搜索Webbrowser查看源代码,你会发现。很多功能都是由一个叫做AxWebbrowser的类是实现的。很明显,webbrowser大部分都是Com控件的包装。
找到Navigate,看看是具体代码

我们继续深挖
private void PerformNavigateHelper(string urlString, bool newWindow, string targetFrameName, byte[] postData, string headers)
{
object objUrlString = (object)urlString;
object objFlags = (object) (newWindow ? : );
object objTargetFrameName = (object)targetFrameName;
object objPostData = (object)postData;
object objHeaders = (object)headers;
PerformNavigate2(ref objUrlString, ref objFlags, ref objTargetFrameName, ref objPostData, ref objHeaders);
} private void PerformNavigate2(ref object URL, ref object flags, ref object targetFrameName, ref object postData, ref object headers)
{
try {
this.AxIWebBrowser2.Navigate2(ref URL, ref flags, ref targetFrameName, ref postData, ref headers);
}
catch (COMException ce) {
if ((uint)unchecked(ce.ErrorCode) != (uint)unchecked(0x800704c7)) {
// "the operation was canceled by the user" - navigation failed
// ignore this error, IE has already alerted the user.
throw;
}
}
}
跳转都是用一个方法。
最终实现的是一个叫做PerformNaviagate2内的AxIWebbrowser2所实现的
继续深挖
最后发现在一个名为UnsafeNativeMethods的类中
这个类是用来做什么呢?
是实现win32API和COM的。(说句心里话写桌面软件,微软心里面还是C++是亲儿子。多少懂一些C++没有错。)
[DispId()] void Navigate2([In] ref object URL, [In] ref object flags,
[In] ref object targetFrameName, [In] ref object postData,
[In] ref object headers);
嗯,看起来似乎就是普通的导航连接啊。
到这里就是很明显了,Navigate就是负责普通的导航,如果是遇到弹出窗口等 基本不好用的
那我们该如何正确的处理呢?
准确的说,我们是想在网页弹出新窗口或者跳转新网页的时候,将其强制的定位到一个网页,让其不弹出新的窗口。
所以我们重新回到了Webbrowser 了
我们发现Webbrowser继承了WebbroweserBase
我们来看看父类中的函数
果不其然发现了重点
/// <include file='doc\WebBrowserBase.uex' path='docs/doc[@for="WebBrowserBase.CreateSink"]/*' />
/// <devdoc>
/// <para>
/// This will be called when we are ready to start listening to events.
/// Inheritors can override this method to hook their own connection points.
/// </para>
/// </devdoc>
protected virtual void CreateSink() {
}
百度翻译了一下

哈,找到了我们该如何触发事件的地方了。这意思就是事件发生时,Webbrowser会做的一些事情。
很明显子类肯定要重写这个方法。重新到Webbrowser寻找这个方法
protected override void CreateSink() {
            object ax = this.activeXInstance;
            if (ax != null) {
                webBrowserEvent = new WebBrowserEvent(this);
                webBrowserEvent.AllowNavigation = AllowNavigation;
                this.cookie = new AxHost.ConnectionPointCookie(ax, webBrowserEvent,
                        typeof(UnsafeNativeMethods.DWebBrowserEvents2));
            }
        }
来看一下啊所有的参数都是些什么
this.activeXInstance;
//这是Webbrowser的父类一个参数,意思是获取基础 ActiveX WebBrowser 控件。 webBrowserEvent = new WebBrowserEvent(this);
webBrowserEvent.AllowNavigation = AllowNavigation;
//这两个都是一个实例,只不过在设置参数。
//WebBrowserEvent是什么?
//他是实现了 //StandardOleMarshalObject,UnsafeNativeMethods.DWebBrowserEvents2的类
this.cookie = new AxHost.ConnectionPointCookie(ax, webBrowserEvent,
typeof(UnsafeNativeMethods.DWebBrowserEvents2));
//创建给定接口类型的连接点。
//将调用实现该接口的托管代码接收器。
到现在也说了很多 我们来理一下思路

Navigate可以排除掉了。不是我们想要的。
那是什么地方呢?
我们来看看这个WebBrowserEvent类都是实现了方法
 public void BeforeNavigate2(object pDisp, ref object urlObject, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel) {
                Debug.Assert(parent != null, "Parent should have been set");
                //Note: we want to allow navigation if we haven't already navigated.
                if (AllowNavigation || !haveNavigated)
                {
                    Debug.Assert(urlObject == null || urlObject is string, "invalid url type");
                    Debug.Assert(targetFrameName == null || targetFrameName is string, "invalid targetFrameName type");
                    Debug.Assert(headers == null || headers is string, "invalid headers type");
                    //
                    // Due to a bug in the interop code where the variant.bstr value gets set
                    // to -1 on return back to native code, if the original value was null, we
                    // have to set targetFrameName and headers to "".
                    if (targetFrameName == null) {
                        targetFrameName = "";
                    }
                    if (headers == null) {
                        headers = "";
                    }
                    string urlString = urlObject == null ? "" : (string)urlObject;
                    WebBrowserNavigatingEventArgs e = new WebBrowserNavigatingEventArgs(
                        new Uri(urlString), targetFrameName == null ? "" : (string)targetFrameName);
                    this.parent.OnNavigating(e);
                    cancel = e.Cancel;
                }
                else
                {
                    cancel = true;
                }
            }
 public void NewWindow2(ref object ppDisp, ref bool cancel) {
                CancelEventArgs e = new CancelEventArgs();
                this.parent.OnNewWindow(e);
                cancel = e.Cancel;
            }
到这里大致过程就明了。
深层的跳转,新开窗口都这里。
我们现在只有能重写以上这两个就可以了。
最后 我们再来整理一下全部的思路

理解了大致的思路,我们就编写代码了。
思路就是
手写编写DWwebbrowserEvent2的接口,编写两个方法。
手写WebbrowserEvent类,实现DW接口。还需要继承StandardOleMarshalObject类
剩下就重写CreateSinK方法了。这个只需要继承Webbrowser就好了。
为了重写定位或者跳转网页,很明显我们还需要一个类来实现webbrowser的url。
而且还需要实现CancelEventArgs类来设置是否取消事件。
public class WebBrowserUrl : CancelEventArgs
{
public string Url { get; } public string Frame { get; } public WebBrowserUrl(String url, String frame) : base()
{
this.Url = url;
this.Frame = frame;
} }
public class NewWebBrwser : System.Windows.Forms.WebBrowser
{
System.Windows.Forms.AxHost.ConnectionPointCookie cookie;
NewWebBrowserEvent events; public event EventHandler BeforeNavigate; public event EventHandler BeforeNewWindow; protected override void CreateSink()
{
base.CreateSink();//还是需要源
events = new NewWebBrowserEvent(this);
cookie = new AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(DWebBrowserEvents2));
}
protected override void DetachSink()
{
if (null != cookie)
{
cookie.Disconnect();
cookie = null;
}
base.DetachSink();
}
public void OnBeforeNavigate(string url, string frame, out bool cancel)
{ WebBrowserUrl webBrowserUrl = new WebBrowserUrl(url, frame);
BeforeNavigate?.Invoke(this, webBrowserUrl);
cancel = webBrowserUrl.Cancel;
}
public void OnBeforeNewWindow(string url, out bool cancel)
{ WebBrowserUrl webBrowserUrl = new WebBrowserUrl(url, null);
BeforeNewWindow?.Invoke(this, webBrowserUrl);
cancel = webBrowserUrl.Cancel; } }
public class NewWebBrowserEvent : System.Runtime.InteropServices.StandardOleMarshalObject, DWebBrowserEvents2
{
private NewWebBrwser webBrowser; public NewWebBrowserEvent(NewWebBrwser newWebBrowser) => webBrowser = newWebBrowser; public void BeforeNavigate2(object pDisp, ref object urlObject, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel) => webBrowser.OnBeforeNavigate((string)urlObject, (string)targetFrameName, out cancel); //当高于IE6时使用
public void NewWindow3(object pDisp, ref bool cancel, ref object flags, ref object URLContext, ref object URL) => webBrowser.OnBeforeNewWindow((string)URL, out cancel);
} //下面这些特性都是古老的COM要用的
[System.Runtime.InteropServices.ComImport(), System.Runtime.InteropServices.Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"),
System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch),
System.Runtime.InteropServices.TypeLibType(System.Runtime.InteropServices.TypeLibTypeFlags.FHidden)]
public interface DWebBrowserEvents2
{
[System.Runtime.InteropServices.DispId()]
void BeforeNavigate2(object pDisp, ref object urlObject, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel); //当高于IE6时使用 //本来应该还有一个NewWindow2 太古老 根本用不上了
[System.Runtime.InteropServices.DispId()]
void NewWindow3([System.Runtime.InteropServices.In,System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)]
object pDisp,
[System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out]
ref bool cancel,
[System.Runtime.InteropServices.In]
ref object flags,
[System.Runtime.InteropServices.In]
ref object URLContext,
[System.Runtime.InteropServices.In]
ref object URL);
}
使用方式
NewWebBrwser Brwser = new NewWebBrwser();
public Form1()
{
InitializeComponent(); Brwser.Url = new Uri("http://www.baidu.com");
Brwser.BeforeNewWindow += Brwser_BeforeNewWindow;
Brwser.BeforeNavigate += Brwser_BeforeNavigate;
this.Controls.Add(Brwser);
} private void Brwser_BeforeNewWindow(object sender, EventArgs e)
{
WebBrowserUrl newWeb = e as WebBrowserUrl; Brwser.Navigate(newWeb.Url); newWeb.Cancel = true;//取消转跳事件
} private void Brwser_BeforeNavigate(object sender, EventArgs e)
{ }
强制所有网页链接在同一页面打开或者在TabControl中弹出新窗口的更多相关文章
- C# WinForm Webbrowser 强制所有网页链接在同一页面打开或者在TabControl中弹出新窗口(续)
		
上面那个文写的如同粑粑一样 效果图 Winfrom 中添加这个类就好了 using System; using System.Collections.Generic; using System.Com ...
 - C# WPF Webbrowser 强制所有网页链接在同一页面打开
		
只要搞懂Winform的 WPF稍微改一改就可以了 主类:负责跳转的 using System; using System.Collections.Generic; using System.Com ...
 - 【Electron】Electron开发入门(七):打开本地文件或者网页链接 and webview里操纵electron api
		
1.打开本地文件或者网页链接 // 打开系统本地文件 const {shell} = require('electron'); // Open a local file in the default ...
 - markdown语法链接新页面打开
		
我们在写markdown语法的文章时,经常会写超链接,默认markdown的写法超链接打开页面都是在当前页面,对自己页面的访问不是很好,所以我们一般都希望在新页面打开.但是据我对markdwon的语法 ...
 - Chrome 禁止从页面打开 Data URI 网址了
		
现如今,网民的网络账户被盗,很有可能是被“钓鱼”了.去年的一份安全报告中指出:“近85%的资金损失是通过钓鱼网址泄露支付信息造成的”. 传统的钓鱼网站通常是申请一个和被冒充网站相似的域名,比如 tao ...
 - web页面打开本地app(判断是否安装)
		
在应用宝中有APP申请链接: //是否可以打开App不可以跳则到下载页 $(".downNow button").on("click",function(){ ...
 - ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml)  用javascript在客户端删除某一个cookie键值对  input点击链接另一个页面,各种操作。  C# 往线程里传参数的方法总结  TCP/IP 协议  用C#+Selenium+ChromeDriver 生成我的咕咚跑步路线地图  (转)值得学习百度开源70+项目
		
ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为 ...
 - 纯前端下载pdf链接文件,而不是打开预览的解决方案
		
纯前端下载pdf链接文件,而不是打开预览的解决方案 一,介绍与需求 1.1,介绍 XMLHttpRequest 用于在后台与服务器交换数据.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行 ...
 - iOS H5页面打开APP技术总结
		
iOS端H5页面打开APP的方式目前主要有两种:URL Scheme和Universal Links.其中Universal Links是iOS9.0以后推出的一种新的方案,由于它需要在iOS9.0以 ...
 
随机推荐
- 1121 Damn Single
			
题意: 给出n对情侣,然后给出聚会上的m个人,问这m个人中有几个人事落单的. 思路: 首先,开一个数组couple[]存储情侣间的映射关系:然后,用exist[]标记聚会上出现过的人:最后遍历0~N, ...
 - oracle里的统计信息
			
1 oracle里的统计信息 Oracle的统计信息是这样的一组数据,存储在数据字典,从多个维度描述了oracle数据库对象的详细信息,有6种类型 表的统计信息:记录数.表块的数量.平均行长度等 索引 ...
 - c语言活动记录-图解(一)
			
来源: 1.<代码揭秘>第六章函数与函数调用 2.http://blog.csdn.net/zhuliting/article/details/6839233 引入话题: 局部变量是动态分 ...
 - chkdsk工具怎么修复
			
对于一些硬盘存储问题,即使windows自带的系统工具,也可以将其修复,比如chkdsk程序.请阅读下文,了解如何使用chkdsk来修复简单的硬盘问题. 工具/原料 windows7 chkdsk 方 ...
 - Hadoop的HA机制
			
前言:正式引入HA机制是从hadoop2.0开始,之前的版本中没有HA机制 1. HA的运作机制 (1)hadoop-HA集群运作机制介绍 所谓HA,即高可用(7*24小时不中断服务) 实现高可用最关 ...
 - Halcon学习之两幅图像处理
			
sub_image ( ImageMinuend, ImageSubtrahend : ImageSub : Mult, Add : ) 对两幅图像做减法 g' := (g1 - g2) * Mu ...
 - 开启mysql远程访问
			
一.登陆mysql以后执行以下命令: GRANT ALL ON *.* TO username@'%' IDENTIFIED BY 'password' WITH GRANT OPTION; FLUS ...
 - Ubuntu16.04安装之后的几个设置
			
Ubuntu16.04安装之后的几个设置 Ubuntu16.04界面很漂亮,但是安装之后,需要做如下几个简单的设置,这样用的时候会更加顺畅. 1.中文支持 在右上角有一个齿轮,点击->Syste ...
 - Linux主机名的设置
			
Linux主机名的设置 Linux主机名的设置步骤如下: 1.在/etc/sysconfig/network文件中修改HOSTNAME的值为要设置的主机名. HOSTNAME=myhost 2.在/e ...
 - (java基础)抽象类加泛型的理解
			
今天在群里问了个基础问题,挨喷了..这更加激起了我对知识的渴望.也在此铭记一下,将来有经验了要对刚入门的童鞋们严格点,简单的东西要自己看...唉,程序员何苦为难程序猿呢.. 接下来简单总结下这个万能的 ...