对ExtendedWebBrowser的再扩展

<=======================
做者: 谭剑
关键字:C#,.NET,WebBrowser,ExtendedWebBrowser,浏览器,彻底载入,新窗口,脚本
=======================>
.NET 2.0 提供了一个新的WebBrowser控件.该WebBrowser控件为咱们带来了许多很是实用的新特性.举个简单的例子:
            HtmlDocument htmlDoc = webBrowser.Document;
            HtmlElement btnElement = htmlDoc.All["btnClose"];
            if (btnElement != null)
            {
                btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click);
            }
仅需相似这样简单易懂的几行代码,即可实现Windows Forms对Web页面各个HTML元素各类事件的响应.
在Windows Forms与Web页面交互这方面,在功能与易用性上,该WebBrowser控件达到了一个史无前例的高度.这样的特性,就已经为咱们在进行相关的Windows Forms程序开发时选用它提供了足够的理由.
很显然,WebBrowser控件是对SHDocVw的AxWebBrowser这个非托管的ActiveX控件进行了封装.可是在实用中,咱们很快能够发现,其在提供新特性的同时,并无所有暴露出AxWebBrowser原有的属性和方法,在不少状况下,这样就使得它显得有点捉襟见肘了.
jlandheer对这个WebBrowser控件进行了扩展( 见Extended .NET 2.0 WebBrowser Control).可是该ExtendedWebBrowser也仅仅是提供了一个结构和思路,真正实现的扩展也比较有限.
根据实际的须要,我又对ExtendedWebBrowser进行了再扩展.
判断页面是否彻底载入:
1.新增DocumentCompleted事件:
在类ExtendedWebBrowser里面事件部分的末尾添加以下代码:
        public new event EventHandler<BrowserExtendedNavigatingEventArgs> DocumentCompleted;
        protected void OnDocumentCompleted(BrowserExtendedNavigatingEventArgs e)
        {
            if (e == null)
                throw new ArgumentNullException("e");
            if (this.DocumentCompleted != null)
                this.DocumentCompleted(this, e);
        }
2.实现public void DocumentComplete(object pDisp, ref object URL)方法:
 在文件ExtendedWebBrowser.cs class WebBrowserExtendedEvents : UnsafeNativeMethods.DWebBrowserEvents2 的 Unused events区域里面能够找到未实现的方法:
            public void DocumentComplete(object pDisp, ref object URL)
            {
            }
将其剪贴到已实现的方法部分的末尾,并修改成:
            public void DocumentComplete(object pDisp, ref object URL)
            {
                BrowserExtendedNavigatingEventArgs args = new BrowserExtendedNavigatingEventArgs(pDisp, new Uri(URL.ToString()), null, UrlContext.None);
                _Browser.OnDocumentCompleted(args);
            }
3.修改原有DocumentCompleted事件的响应:
因为上面重写了DocumentCompleted事件,因此须要修改原有的DocumentCompleted事件响应:
修改类BrowserControl中
            _browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(_browser_DocumentCompleted);
为:
            _browser.DocumentCompleted += new EventHandler<BrowserExtendedNavigatingEventArgs>(_browser_DocumentCompleted);
修改
        void _browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
为:
        void _browser_DocumentCompleted(object sender, BrowserExtendedNavigatingEventArgs e)
其它相应的地方进行一样的修改.
4.发送DocumentCompleted事件到最外层:
该ExtendedWebBrowser演示程序经过类WindowManager管理了一个标签浏览器,若是须要,咱们能够将事件发送到最外层,提供给MainForm使用.
将WindowManager中的代码
        void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            CheckCommandState();
        }
改为:
        void WebBrowser_DocumentCompleted(object sender, BrowserExtendedNavigatingEventArgs e)
        {
            OnDocumentCompleted(sender, e);
            CheckCommandState();
        }
        public event EventHandler<BrowserExtendedNavigatingEventArgs> DocumentCompleted;
        /// <summary>
        /// Raises the DocumentCompleted event
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnDocumentCompleted(object sender, BrowserExtendedNavigatingEventArgs e)
        {
            if (DocumentCompleted != null)
                DocumentCompleted(sender, e);
        }
5.在须要的地方响应DocumentCompleted事件:
如:
            _windowManager.DocumentCompleted += new EventHandler<BrowserExtendedNavigatingEventArgs>(_windowManager_DocumentCompleted);
.
.
.
        void _windowManager_DocumentCompleted(object sender, BrowserExtendedNavigatingEventArgs e)
        {
            // 页面彻底载入
            if (e.AutomationObject == ((ExtendedWebBrowser)sender).Application)
            {
                MessageBox.Show("DocumentCompleted:" + e.Url.ToString());
            }
        }
注意:必须e.AutomationObject == ((ExtendedWebBrowser)sender).Application才说明页面已经彻底载入.因为Web页面可能为多个框架嵌套的(可能还有其它的状况,还没有深究),那么载入过程当中对应会有多个DocumentCompleted事件,而只有页面彻底载入了,才会e.AutomationObject == ((ExtendedWebBrowser)sender).Application.这就是为何要费尽千辛万苦把pDisp传递出来的缘由.
不弹出页面,仍在当前浏览器浏览:
ExtendedWebBrowser中的StartNewWindow事件在弹出新页面的时候触发.其扩展的事件数据BrowserExtendedNavigatingEventArgs中已经提供了上下文,新页面的URL,ppDisp等所需的数据.因此咱们只需改动类BrowserControl中的方法
        void _browser_StartNewWindow(object sender, BrowserExtendedNavigatingEventArgs e)
为:
        void _browser_StartNewWindow(object sender, BrowserExtendedNavigatingEventArgs e)
        {
            // Here we do the pop-up blocker work
            // 这些弹出窗口过滤的代码若是不须要,就所有删除掉
            if (allowPopup)
            {
                // Check wheter it's a HTML dialog box. If so, allow the popup but do not open a new tab
                if (!((e.NavigationContext & UrlContext.HtmlDialog) == UrlContext.HtmlDialog))
                {
                    ExtendedWebBrowser ewb = mf.WindowManager.New(false);
                    // The (in)famous application object
                    e.AutomationObject = ewb.Application;
                }
            }
            //在这里使用: e.AutomationObject = _browser.Application;彷佛没有做用,
            //可是他原来的代码e.AutomationObject = ewb.Application;是有用的(见上面注释)
            //不知道是什么缘由,只好暂时采用这个办法
            _browser.Navigate(e.Url);
            e.Cancel = true;
        }
这里若是使用e.AutomationObject = _browser.Application的话,彷佛同他原来的代码e.AutomationObject = ewb.Application惟一的区别就是一个browser是浏览过页面了的,一个browser.是新建的,难道会同这个有关? 哪位若是知道缘由,还请不吝赐教!
不弹出脚本错误提示框:
ExtendedWebBrowser经过响应事件WebBrowser.Document.Window.Error来捕获脚本错误提示框弹出的消息:
        void _browser_DownloadComplete(object sender, EventArgs e)
        {
            // Check wheter the document is available (it should be)
            if (this.WebBrowser.Document != null)
            {
                // Subscribe to the Error event
                this.WebBrowser.Document.Window.Error += new HtmlElementErrorEventHandler(Window_Error);
.
.
.
        void Window_Error(object sender, HtmlElementErrorEventArgs e)
        {
            // We got a script error, record it
            ScriptErrorManager.Instance.RegisterScriptError(e.Url, e.Description, e.LineNumber);
            // Let the browser know we handled this error.
            e.Handled = true;
        }
可是彷佛仍是因为前面提到的Web页面存在多个框架嵌套的缘由,其可以捕获到的消息比较有限,有很大一部分脚本错误提示消息仍然会捕获不到.这样一个问题困扰了我几天.后来忽然发现ExtendedWebBrowser里面有以下的代码:
        /// <summary>
        /// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code.  
        /// Called by the control when the underlying ActiveX control is created.  
        /// </summary>
        /// <param name="nativeActiveXObject"></param>
        [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
        protected override void AttachInterfaces(object nativeActiveXObject)
        {
            this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject;
            base.AttachInterfaces(nativeActiveXObject);
        }
原来AxWebBrowser在这里!因而简单的设置了axIWebBrowser2.Silent属性:
        protected override void AttachInterfaces(object nativeActiveXObject)
        {
            this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject;
            this.axIWebBrowser2.Silent = true;//不弹脚本错误框
            base.AttachInterfaces(nativeActiveXObject);
        }
再也不弹出任何提示框,Silent属性真的是恰如其名,整个世界清静了...*^_^*
从上面的内容能够看出,ExtendedWebBrowser截取了AxWebBrowser接口.那么,不难想象,虽然个人"再扩展"只扩展了如上三项功能,其实咱们如今能够作咱们任何想作的!
<=======================
做者: 谭剑
相关文章
相关标签/搜索