原文:WebBrowser控件使用技巧分享

在发布“淘宝登货员”时发现不少朋友对WebBrowser控件比较感兴趣,故在此分享一下使用心得。

首先分享一个WebBrowser的扩展类(此类所需的dll将在文章末尾提供下载),大家最好都使用这个类来替代.Net框架中的WebBrowser类,它提供了两个扩展功能:

1.屏蔽错误脚本提示。修正了WebBrowser控件本身屏蔽错误不全的问题,由启明提出,原文:http://www.cnblogs.com/hobe/archive/2007/01/14/619906.html

2.扩展NewWindow事件。修正了WebBrowser控件本身的NewWindow事件不提供新窗口Url的问题,通过新增的BeforeNewWindow事件予以支持,由佳文转载并整理,原文:http://www.cnblogs.com/yjwgood/archive/2009/02/09/1386789.html

整合后的代码如下:

public class ExWebBrowser : System.Windows.Forms.WebBrowser

{

private SHDocVw.IWebBrowser2 Iwb2;

protected override void AttachInterfaces(object nativeActiveXObject)

{

Iwb2 = (SHDocVw.IWebBrowser2)nativeActiveXObject;

Iwb2.Silent = true;

base.AttachInterfaces(nativeActiveXObject);

}

protected override void DetachInterfaces()

{

Iwb2 = null;

base.DetachInterfaces();

}

System.Windows.Forms.AxHost.ConnectionPointCookie cookie;

WebBrowserExtendedEvents events;

//This method will be called to give you a chance to create your own event sink

protected override void CreateSink()

{

//MAKE SURE TO CALL THE BASE or the normal events won't fire

base.CreateSink();

events = new WebBrowserExtendedEvents(this);

cookie = new System.Windows.Forms.AxHost.ConnectionPointCookie(this.ActiveXInstance, events, typeof(DWebBrowserEvents2));

}

protected override void DetachSink()

{

if (null != cookie)

{

cookie.Disconnect();

cookie = null;

}

base.DetachSink();

}

//This new event will fire when the page is navigating

public event EventHandler BeforeNavigate;

/// <summary>

/// 可用于替代原来的NewWindow事件,新增了事件的Url参数支持。

/// </summary>

[CategoryAttribute("操作"), DescriptionAttribute("经过扩展的NewWindow事件,使用继承后的WebBrowserExtendedNavigatingEventArgs类型参数实现Url参数支持")]

public event EventHandler BeforeNewWindow;

protected void OnBeforeNewWindow(string url, out bool cancel)

{

EventHandler h = BeforeNewWindow;

WebBrowserExtendedNavigatingEventArgs args = new WebBrowserExtendedNavigatingEventArgs(url, null);

if (null != h)

{

h(this, args);

}

cancel = args.Cancel;

}

protected void OnBeforeNavigate(string url, string frame, out bool cancel)

{

EventHandler h = BeforeNavigate;

WebBrowserExtendedNavigatingEventArgs args = new WebBrowserExtendedNavigatingEventArgs(url, frame);

if (null != h)

{

h(this, args);

}

//Pass the cancellation chosen back out to the events

cancel = args.Cancel;

}

//This class will capture events from the WebBrowser

class WebBrowserExtendedEvents : System.Runtime.InteropServices.StandardOleMarshalObject, DWebBrowserEvents2

{

ExWebBrowser _Browser;

public WebBrowserExtendedEvents(ExWebBrowser browser) { _Browser = browser; }

//Implement whichever events you wish

public void BeforeNavigate2(object pDisp, ref object URL, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel)

{

_Browser.OnBeforeNavigate((string)URL, (string)targetFrameName, out cancel);

}

public void NewWindow3(object pDisp, ref bool cancel, ref object flags, ref object URLContext, ref object URL)

{

_Browser.OnBeforeNewWindow((string)URL, out cancel);

}

}

[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)]

void BeforeNavigate2(

[System.Runtime.InteropServices.In,

System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)] object pDisp,

[System.Runtime.InteropServices.In] ref object URL,

[System.Runtime.InteropServices.In] ref object flags,

[System.Runtime.InteropServices.In] ref object targetFrameName, [System.Runtime.InteropServices.In] ref object postData,

[System.Runtime.InteropServices.In] ref object headers,

[System.Runtime.InteropServices.In,

System.Runtime.InteropServices.Out] ref bool cancel);

[System)]

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);

}

}

public class WebBrowserExtendedNavigatingEventArgs : CancelEventArgs

{

private string _Url;

public string Url

{

get { return _Url; }

}

private string _Frame;

public string Frame

{

get { return _Frame; }

}

public WebBrowserExtendedNavigatingEventArgs(string url, string frame)

: base()

{

_Url = url;

_Frame = frame;

}

}

技巧1:在当前窗口内打开目标为新窗口的超链接

通过上述的扩展类支持得以实现,增加BeforeNewWindow事件的处理函数以进行处理:

void webBrowser1_BeforeNewWindow(object sender, EventArgs e)

{

WebBrowserExtendedNavigatingEventArgs eventArgs = e as WebBrowserExtendedNavigatingEventArgs;

if (eventArgs.Url.ToLower() != "about:blank")

webBrowser1.Navigate(eventArgs.Url);

eventArgs.Cancel = true;

}

这种方法的弊病在于可能会错误地转向到网站的弹窗广告,为了规避此问题,可以强制取消一切弹出窗口,采取另一种方法实现当前窗口内打开新窗口超链接,增加DocumentCompleted事件的处理函数以进行处理:

void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)

{

if (webBrowser1.ReadyState > WebBrowserReadyState.Interactive)

{

foreach (HtmlElement f in webBrowser1.Document.Links)

{

var s = f.GetAttribute("target");

if (s != null && s.ToLower() == "_blank") f.SetAttribute("target", "_self");

}

}

}

此方法将遍历所有<a>元素,修改其目标为当前窗口,但是此方法又会引发新的问题,即如果页面中某些元素长时间都未加载完成时,此事件将迟迟不会被引发,也就是说用户必须要等到页面完完全全加载完毕之后才可能在当前窗口内打开新窗口超链接。

根据一些人的经验,DocumentCompleted事件会在每次加载网页的过程中触发两次,第一次触发时WebBrowser控件的ReadyState属性应为Interactive,第二次则为Complete,根据注释来看,Interactive应该是代表页面加载初步完成,已具有基本交互能力的状态,这时应当是理想的编辑状态,但我尝试将代码中的if (webBrowser1.ReadyState > WebBrowserReadyState.Interactive)修改为if (webBrowser1.ReadyState >= WebBrowserReadyState.Interactive),并没有什么明显效果,页面上的超链接还是要等待全部加载之后才会被修改。

为此我还尝试过在Navigated事件中进行处理,也不起作用。希望高人能对此给出完美的解决方案

技巧2:获取状态栏信息

增加StatusTextChanged事件处理函数进行处理:

void webBrowser1_StatusTextChanged(object sender, EventArgs e)

{

label1.Text = webBrowser1.StatusText;

}

技巧3:页面转向后改变地址栏地址

在Navigated事件处理函数中改变地址栏地址是最恰当的:

private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)

{

textBox1.Text = webBrowser1.Url.ToString();

}

技巧4:正确设置单选框的方法

建议使用执行单击事件的方式来设置单选框,而不是修改属性:

webBrowser1.Document.GetElementById("RBT_A").InvokeMember("click");

技巧5:正确设置联动型下拉列表的方法

比较常见的联动型多级下拉列表就是省/市县选择了,这种情况下直接设置选择项的属性不会触发联动,需要在最后执行触发事件函数才能正常工作:

foreach (HtmlElement f in s.GetElementsByTagName("option"))

{

if (f.InnerText == "北京")

{

f.SetAttribute("selected", "selected");

}

else

{

f.SetAttribute("selected", "");

}

}

s.RaiseEvent("onchange");

此方法来源于:http://topic.csdn.net/u/20070309/11/aef46651-a15a-4777-b832-e71b09a7b9e0.html

技巧6:使用延迟等待服务器交互

有时会遇到联动型下拉列表需要同服务器交互的情况,如果只在一个函数里连续进行设置,往往会失败,因为代码执行速度很快,这期间页面还没有从服务器得到并装载数据。

这时候应当通过使用Timer等方法设置延迟间隔,再进行更改,需注意的是,不应当使用Sleep方法停止当前线程的执行以求达到延迟目的,因为WebBrowser控件也处于当前线程内,Sleep会同时暂停WebBrowser控件的运作。

还有一点需要注意,就是如果程序内用到多个Timer的话,有可能引发不可预料的错乱,详情及解决办法可参看我的前一篇文章

尚未解决的问题

有一个问题一直困扰我,始终也没找到相关的资料:

我现在可以通过WebBrowser实现对各种Html元素的操控,唯独无法控制Html的上传控件,即:

<input type="file" size="50"/>

应当是出于安全原因,JS代码无法访问和设置此控件所选择的文件路径,这是符合情理的,但是WebBrowser中也没能找到相关的支持,这样就无法实现自动上传等功能,希望有高手能指出解决办法。

还有一个似乎是无解的问题,就是读取和操作页面内的框架页或内嵌页的问题,很多人发出疑问,但始终没找到解决方法,此方面最典型的应用就是自动点嵌入式广告功能了,而现在不但无法点击,甚至都无法获取框架页的代码等信息。

其他相关技术资料

对ExtendedWebBrowser的再扩展:http://hi.baidu.com/tanjian/blog/item/d46b83021772a10f4afb511c.html

C#利用WebBrowser操作HTML:http://hi.baidu.com/lightrock/blog/item/c4a61d2bf6dde5fce7cd40fb.html

关于C#.net中WebBrowser如何处理多框架结构页面下载完成问题:http://blog1.poco.cn/myBlogDetail-htx-id-381745-userid-7940008-pri--n-0.shtml

利用webBrowser获取框架内Html页面内容:http://www.cnblogs.com/tishifu/archive/2007/12/10/990071.html

WebBrowser控件的简单应用2:http://www.cnblogs.com/dlwang2002/archive/2007/04/11/709078.html

WebBrowser控件应用:弹出新窗体和关闭窗口:http://www.cnblogs.com/dlwang2002/archive/2007/04/14/713499.html

.Net 2.0实例学习:WebBrowser页面与WinForm交互技巧:http://smalldust.cnblogs.com/archive/2006/03/08/345561.html

WebBrowser控件禁用超链接转向、脚本错误提示、默认右键菜单和快捷键:http://www.zu14.cn/2008/11/19/webbrowser/

下载Interop.SHDocVw.dll:http://cid-0612298d2255e149.skydrive.live.com/self.aspx/.Public/%e6%96%87%e6%a1%a3/Interop.SHDocVw.zip

下载本文的PDF版本: http://cid-0612298d2255e149.skydrive.live.com/self.aspx/.Public/%e6%96%87%e6%a1%a3/WebBrowser%e6%8e%a7%e4%bb%b6%e4%bd%bf%e7%94%a8%e6%8a%80%e5%b7%a7%e5%88%86%e4%ba%ab.pdf

WebBrowser控件使用技巧分享的更多相关文章

  1. C#WebBrowser控件使用教程与技巧收集--苏飞收集

    C#WebBrowser控件使用教程与技巧收集--苏飞收集 先来看看常用的方法 Navigate(string urlString):浏览urlString表示的网址 Navigate(System. ...

  2. Webbrowser控件史上最强技巧全集

    原文:Webbrowser控件史上最强技巧全集 Webbrowser控件史上最强技巧全集 VB调用webbrowser技巧集 1.获得浏览器信息: Private Sub Command1_Click ...

  3. C#webbrowser控件技巧(取得javascript变量值,禁止显示脚本错误)

    C#中的webbrowser控件比较好用. 下面本人搜索整理的几个小技巧. 1. 从C#中取得javascript的变量值. using mshtml;using System.Reflection; ...

  4. C# WebBrowser控件使用教程与技巧收集

    常用的方法 Navigate(string urlString):浏览urlString表示的网址 Navigate(System.Uri url):浏览url表示的网址 Navigate(strin ...

  5. C#WebBrowser控件使用教程与技巧

    获取非input控件的值 webBrowser1.Document.All["控件ID"].InnerText;或webBrowser1.Document.GetElementBy ...

  6. WebBrowser控件使用详解

    原文:WebBrowser控件使用详解 方法 说明 GoBack 相当于IE的“后退”按钮,使你在当前历史列表中后退一项 GoForward 相当于IE的“前进”按钮,使你在当前历史列表中前进一项 G ...

  7. .Net2.0 --Winform结合WebBrowser控件和Socket老技术来实现另类Push~

    原文:.Net2.0 --Winform结合WebBrowser控件和Socket老技术来实现另类Push~ 目前的企业级开发比较流行的是Web2.0技术,但是由于Web技术基于请求--响应的交互模式 ...

  8. 更改WebBrowser控件的用户代理

    我试图在Winforms应用程序中更改WebBrowser控件的UserAgent. 我已成功使用以下代码实现此目的: [DllImport("urlmon.dll", CharS ...

  9. C#:WebBrowser控件的使用教程及相关问题整理

    推荐阅读: C#WebBrowser控件使用教程与技巧收集--苏飞收集 C# WebBrowser强制在本窗口打开,禁止在新窗口打开 C# WebBrowser禁止在新窗口打开,强制在本窗口打开(多种 ...

随机推荐

  1. css+js整站变灰(兼容IE7+)

    原文:css+js整站变灰(兼容IE7+) 历年大型地震等自然灾害来临过后,各大网站整站都变成灰色以悼念逝去的生命,那么这种整站变灰的效果是怎么做到的? 重写一套css?NO,即便你有这个时间重写,那 ...

  2. SQLSERVER PRINT语句的换行

    原文:SQLSERVER PRINT语句的换行 SQLSERVER  PRINT语句的换行 想在输出的PRINT语句里面换行,可以这样做 /* SQL的换行 制表符 CHAR(9) 换行符 CHAR( ...

  3. gdb经常使用的命令

    在调试程序的时候,gdb是一柄利器,恰当的使用gdb能够解决掉程序的很多bug. gdb并不检查语法错误.那是gcc或者g++的事情,gdb干的是调试的事情. 说明: (1)gdb 程序名 [core ...

  4. 从电商秒杀与抢购谈Web系统大规模并发

    从电商秒杀与抢购谈Web系统大规模并发 http://www.iamlintao.com/4242.html 一.大规模并发带来的挑战 在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程 ...

  5. 【转】Robot Framework 快速入门

    目录 介绍 概述 安装 运行demo 介绍样例应用程序 测试用例 第一个测试用例 高级别测试用例 数据驱动测试用例 关键词keywords 内置关键词 库关键词 用户定义关键词 变量 定义变量 使用变 ...

  6. 【转】[Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解. [size=1.8em]Handler+Runn ...

  7. Android 建立View 圆角

    虽然很easy,不过还是录制. 混合参观 在drawable文件下 创建一个布局文件corners_bg.xml <?xml version="1.0" encoding=& ...

  8. 大约Java有点感悟---开发商根本上感悟学习

    这些年来一直从事大C.C++,有些局部底.一直想知道更多关于顶级什么. 所以,在工作之余.阅读更多Java哪些方面,还使用了一些建筑结构的一些简单的程序,在这里我想简单谈谈自己的一点感悟. 1.Jav ...

  9. HTML5 3D翻书效果(双面效应)

    最后使用HTML5翻书效果达到测试,比较简单,它的升级版是 最后一个问题: 1)后,原来的页面连环画将成为一面镜子 2)无法实现双面翻书. 3)明显感觉页面似有近遮挡标志. 这次的升级版本号实现过程比 ...

  10. 华丽的网上突出代码组件CodeMirror

    农民之间的代码懒惰性质:愚公绝不能过夜.一劳永逸永远不知疲倦!这是一个代码示例 动态配置,在不同的场景抽象为常见的配置逻辑加,这使得有可能"为一个全球性的代码.代码做搬运工",更糟 ...