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中弹出新窗口的更多相关文章

  1. C# WinForm Webbrowser 强制所有网页链接在同一页面打开或者在TabControl中弹出新窗口(续)

    上面那个文写的如同粑粑一样 效果图 Winfrom 中添加这个类就好了 using System; using System.Collections.Generic; using System.Com ...

  2. C# WPF Webbrowser 强制所有网页链接在同一页面打开

    只要搞懂Winform的  WPF稍微改一改就可以了 主类:负责跳转的 using System; using System.Collections.Generic; using System.Com ...

  3. 【Electron】Electron开发入门(七):打开本地文件或者网页链接 and webview里操纵electron api

    1.打开本地文件或者网页链接 // 打开系统本地文件 const {shell} = require('electron'); // Open a local file in the default ...

  4. markdown语法链接新页面打开

    我们在写markdown语法的文章时,经常会写超链接,默认markdown的写法超链接打开页面都是在当前页面,对自己页面的访问不是很好,所以我们一般都希望在新页面打开.但是据我对markdwon的语法 ...

  5. Chrome 禁止从页面打开 Data URI 网址了

    现如今,网民的网络账户被盗,很有可能是被“钓鱼”了.去年的一份安全报告中指出:“近85%的资金损失是通过钓鱼网址泄露支付信息造成的”. 传统的钓鱼网站通常是申请一个和被冒充网站相似的域名,比如 tao ...

  6. web页面打开本地app(判断是否安装)

    在应用宝中有APP申请链接: //是否可以打开App不可以跳则到下载页 $(".downNow button").on("click",function(){ ...

  7. 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的返回值自动进行各种序列化处理(序列化为 ...

  8. 纯前端下载pdf链接文件,而不是打开预览的解决方案

    纯前端下载pdf链接文件,而不是打开预览的解决方案 一,介绍与需求 1.1,介绍 XMLHttpRequest 用于在后台与服务器交换数据.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行 ...

  9. iOS H5页面打开APP技术总结

    iOS端H5页面打开APP的方式目前主要有两种:URL Scheme和Universal Links.其中Universal Links是iOS9.0以后推出的一种新的方案,由于它需要在iOS9.0以 ...

随机推荐

  1. Py修行路 python基础 (十)装饰器

    装饰器 一.定义 装饰器:顾名思义,就是对某个东西起到装饰修饰的功能. python中的装饰器,其本质上就是一个python函数,它可以让其他函数在不需要任何代码变动的前提下增加额外功能.通俗理解就是 ...

  2. 7.solr学习速成之facet

    Facet 介绍   Facet 是 solr 的高级搜索功能之一 ,可以给用户提供更友好的搜索体验,在搜索关键字的同时 , 能够按照 Facet 的字段进行分组并统计.        比如你上淘宝, ...

  3. kettle init

  4. 在Centos7中安装elasticsearch5.5

    在Centos7中安装elasticsearch5.5 第一步:必须要有jre支持 elasticsearch是用Java实现的,跑elasticsearch必须要有jre支持,所以必须先安装jre ...

  5. BurpSuite—-decoder模块(编码模块)

    一.简介 Burp Decoder是Burp Suite中一款编码解码工具,将原始数据转换成各种编码和哈希表的简单工具,它能够智能地识别多种编码格式采用启发式技术. 二.模块说明 通过有请求的任意模块 ...

  6. linux下静态库和动态库一些东西

    http://www.cnblogs.com/changefuture/archive/2011/12/22/2297460.html Linux  动态链接库和静态库示例 文件预览 文件目录树如下, ...

  7. PHP数据结构之四 一元多项式的相加PHP单链实现

    <?php /** *一元多项式的表示和相加 *一元多项式的表示采用单链表的形式 **/ header("content-type:text/html;charset=gb2312&q ...

  8. QPS、PV和需要部署机器数量计算公式

    QPS:Queries Per Second意思是“每秒查询率”,是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准. TPS是 Transactions ...

  9. 443. String Compression字符串压缩

    [抄题]: Given an array of characters, compress it in-place. The length after compression must always b ...

  10. codefirst 最新策略

    http://www.yunjuu.com/info/76058.html 在原有数据库中使用 CodeFirst ,除了第一次添加实体后要立即执行一次 Enable-Migrations add-m ...