熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

WebBrowser頁與WinForm交互技巧

2022-06-13   來源: .NET編程 

  最近看到博客園入門教學文章比較流行自己最近又偷懶比較多沒啥心得不妨寫一篇沒啥深度的入門文章吧

  話說有了WebBrowser類終於不用自己手動封裝SHDocVw的AxWebBrowser這個ActiveX控件了這個類如果僅僅作為一個和IE一模一樣浏覽器那就太沒意思了(還不如直接用IE呢)那麼無論我們是想做一個定制版IE還是希望利用HTML來做用戶界面(指WinApp而非WebApp許多單機軟件包括Windows的幫助支持中心都是HTML做的)都少不了Windows Form和包含在WebBrowser中的Web頁面的交互本文將通過幾個實際的例子初步介紹一下WinForm和WebBrowser所包含的Web頁面之間的交互

  下面的代碼假設你已經建立了一個Windows Form上面有一個WebBrowser名為webBrowser

  Study Case 用WinForm的Event Handler響應Web頁面的事件

  現在有這樣一個Windows Application它的界面上只有一個WebBrowser顯示一個本地的HTML文件作為界面現在的問題是所有邏輯都可以放在HTML文件裡唯獨關閉按鈕遇到了困難——通常Web頁面是沒有辦法直接控制浏覽器的更不用說結束這個WinForm程序了

  但是Net 當中由Windows Form響應Web頁面的事件已經成為了現實

  在Net 整個HTML文檔以及其包含的各個HTML元素都和一個個HtmlDocumentHtmlElement之類的Net對象對應因此只要找到這個關閉按鈕對應的HtmlElement對象為其click事件添加Event Handler即可

  假設HTML源代碼如下


<html>
<body>
<input type=button id=btnClose value=關閉 />
body>
html>

  那麼找出該按鈕並為之添加Event Handler的代碼如下:


HtmlDocument htmlDoc = webBrowser.Document;
HtmlElement btnElement = htmlDoc.All["btnClose"];
if (btnElement != null)
{
    btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click);
}

  其中HtmlBtnClose_Click是按下Web按鈕時的Event Handler。TW.WINgwIT.cOm

  很簡單吧?那麼稍稍高級一點的——我們都知道一個HTML元素可能有很多各種各樣的事件,而HtmlElement這個類只給出最常用、共通的幾個。那麼,如何響應其他事件呢?這也很簡單,只需要調用HtmlElement的AttachEventHandler就可以了:


btnElement.AttachEventHandler("onclick", new EventHandler(HtmlBtnClose_Click)); 
//這一句等價於上面的btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click); 

  把"onclick"換成其他事件的名字就可以了。例如:


formElement.AttachEventHandler("onsubmit", new EventHandler(HtmlForm_Submit)); 

  Study Case 2:表單(form)的自動填寫和提交 

    要使我們的WebBrowser具有自動填表、甚至自動提交的功能,並不困難。

  假設有一個最簡單的登錄頁面,輸入用戶名密碼,點“登錄”按鈕即可登錄。已知用戶名輸入框的id(或Name,下同)是username,密碼輸入框的id是password,“登錄”按鈕的id是submitbutton,那麼我們只需要在webBrowser的DocumentCompleted事件中使用下面的代碼即可:


HtmlElement btnSubmit = webBrowser.Document.All["submitbutton"];
HtmlElement tbUserid = webBrowser.Document.All["username"];
HtmlElement tbPasswd = webBrowser.Document.All["password"];

if (tbUserid == null || tbPasswd == null || btnSubmit == null)
    return;

tbUserid.SetAttribute("value", "smalldust");
tbPasswd.SetAttribute("value", "12345678");

btnSubmit.InvokeMember("click");

  這裡我們用SetAttribute來設置文本框的“value”屬性,用InvokeMember來調用了按鈕的“click”方法。因為不同的Html元素,其擁有的屬性和方法也不盡相同,所以.Net 2.0提供了統一的HtmlElement來概括各種Html元素的同時,提供了這兩個方法以調用元素特有的功能。關於各種Html元素的屬性和方法一覽,可以查閱MSDN的DHTML Reference。

  ※關於表單的提交,的確還有另一種方法就是獲取form元素而不是button,並用form元素的submit方法:


HtmlElement formLogin = webBrowser.Document.Forms["loginForm"]; 
//…… 
formLogin.InvokeMember("submit"); 

  本文之所以沒有推薦這種方法,是因為現在的網頁,很多都在submit按鈕上添加onclick事件,以對提交的內容做最基本的驗證。如果直接使用form的submit方法,這些驗證代碼就得不到執行,有可能會引起錯誤。

  Study Case 3:查找並選擇文本

  這次我們希望實現一個和IE一模一樣的查找功能,以對Web頁面內的文字進行查找。

  文本查找要借助於TextRange對象的findText方法。但是,。Net裡並沒有這個對象。這是因為,。Net 2.0提供的HtmlDocument,HtmlWindow,HtmlElement等類,只不過是對原有mshtml這個COM組件的不完整封裝,只提供了mshtml的部分功能。所以許多時候,我們仍舊要借助mshtml來實現我們需要的功能。好在這些。Net類都提供了DomDocument這個屬性,使得我們很容易把。Net對象轉換為COM對象使用。下面的代碼演示了如何查找Web頁面的文本。

  (需要添加mshtml的引用,並加上using mshtml;)


    public partial class SearchDemo : Form
    {
        // 建立一個查找用的TextRange(IHTMLTxtRange接口) 
        private IHTMLTxtRange searchRange = null;
        public SearchDemo()
        {
            InitializeComponent();
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            // Document的DomDocument屬性,就是該對象內部的COM對象。 
            IHTMLDocument2 document = (IHTMLDocument2)webBrowser.Document.DomDocument;
            string keyword = txtKeyword.Text.Trim();
            if (keyword == "")
                return;

            // IE的查找邏輯就是,如果有選區,就從當前選區開頭+1字符處開始查找;沒有的話就從頁面最初開始查找。 
            // 這個邏輯其實是有點不大恰當的,我們這裡不用管,和IE一致即可。 
            if (document.selection.type.ToLower() != "none")
            {
                searchRange = (IHTMLTxtRange)document.selection.createRange();
                llapse(true);
                searchRange.moveStart("character", 1);
            }
            else
            {
                IHTMLBodyElement body = (IHTMLBodyElement)document.body;
                searchRange = (IHTMLTxtRange)body.createTextRange();
            }

            // 如果找到了,就選取(高亮顯示)該關鍵字;否則彈出消息。 
            if (searchRange.findText(keyword, 1, 0))
            {
                searchRange.select();
            }
            else
            {
                MessageBox.Show("已搜索到文檔結尾。");
            }
        }
    } 

  到此為止,簡單的查找就搞定了。至於替換功能,看了下一個例子,我相信你就可以觸類旁通輕松搞定了。

  Study Case 4:高亮顯示

  上一個例子中我們學會了查找文本——究跟到底,對Web頁面還是只讀不寫。那麼,如果說要把所有的搜索結果高亮顯示呢?我們很快會想到把所有匹配的文字顏色、背景改一下就可以了。

  首先想到的可能是直接修改HTML文本吧……但是,與SourceCode的高亮顯示不同,我們需要並且只需要高亮頁面中的文本部分。HTML標簽、腳本代碼等等是絕對不應該去改動的。因此我們不能把整個頁面的Source Code讀進來然後replace,那樣有破壞HTML文件結構的可能;我們只能在能夠分離出文本與其他內容(標簽,腳本……)的前提下進行。

  具體方法有很多,下面提供兩個比較簡單的方法。

  方法一:使用TextRange(IHTMLTxtRange)

  有了上一個Case的基礎,相信大家立刻會想到使用TextRange.沒錯,TextRange除了提供查找方法之外,還提供了一個pasteHTML方法,以指定的HTML文本替換當前TextRange中的內容。代碼片斷如下:


    public partial class HilightDemo : Form
    {
        // 定義高亮顯示效果的標簽。
        string tagBefore = "";
        string tagAfter = "";

        // ……

        private void btnHilight_Click(object sender, EventArgs e)
        {
            HtmlDocument htmlDoc = webBrowser.Document;
            string keyword = txtKeyword.Text.Trim();
            if (keyword == "")
                return;

            object oTextRange = htmlDoc.Body.InvokeMember("createTextRange");

            mshtml.IHTMLTxtRange txtrange = oTextRange as mshtml.IHTMLTxtRange;

            while (txtrange.findText(keyword, 1, 4))
            {
                try
                {
                    txtrange.pasteHTML(tagBefore + keyword + tagAfter);
                }
                catch { }
                llapse(false);
            }
        }
    }

  ※這段代碼裡獲取IHTMLTxtRange的方式和上面的例子稍稍不同,其實所謂條條大路通羅馬,本質是一樣的。 

    方法二:使用DOM(文檔對象模型)
將HTML文檔解析為DOM,然後遍歷每個節點,在其中搜索關鍵字並進行相應替換處理即可。


    public partial class HilightDemo : Form
    {
        //……

        private void btnHilight_Click(object sender, EventArgs e)
        {
            HTMLDocument document = (HTMLDocument)webBrowser.Document.DomDocument;
            IHTMLDOMNode bodyNode = (IHTMLDOMNode)webBrowser.Document.Body.DomElement;
            string keyword = txtKeyword.Text.Trim();
            if (keyword == "")
                return;

            HilightText(document, bodyNode, keyword);
        }

        private void HilightText(HTMLDocument document, IHTMLDOMNode node, string keyword)
        {
            // nodeType = 3:text節點
            if (node.nodeType == 3)
            {
                string nodeText = node.nodeValue.ToString();
                // 如果找到了關鍵字
                if (nodeText.Contains(keyword))
                {
                    IHTMLDOMNode parentNode = node.parentNode;
                    // 將關鍵字作為分隔符,將文本分離,並逐個添加到原text節點的父節點
                    string[] result = nodeText.Split(new string[] { keyword }, StringSplitOptions.None);
                    for (int i = 0; i < result.Length - 1; i++)
                    {
                        if (result[i] != "")
                        {
                            IHTMLDOMNode txtNode = document.createTextNode(result[i]);
                            parentNode.insertBefore(txtNode, node);
                        }
                        IHTMLDOMNode orgNode = document.createTextNode(keyword);
                        IHTMLDOMNode hilightedNode = (IHTMLDOMNode)document.createElement("SPAN");
                        IHTMLStyle style = ((IHTMLElement)hilightedNode).style;
                        lor = "black";
                        style.backgroundColor = "yellow";
                        hilightedNode.appendChild(orgNode);

                        parentNode.insertBefore(hilightedNode, node);
                    }
                    if (result[result.Length - 1] != "")
                    {
                            IHTMLDOMNode postNode = document.createTextNode(result[result.Length - 1]);
                            parentNode.insertBefore(postNode, node);
                    }
                    parentNode.removeChild(node);
                } // End of nodeText.Contains(keyword)
            }
            else
            {
                // 如果不是text節點,則遞歸搜索其子節點
                IHTMLDOMChildrenCollection childNodes = node.childNodes as IHTMLDOMChildrenCollection;
                foreach (IHTMLDOMNode n in childNodes)
                {
                    HilightText(document, n, keyword);
                }
            }
        }
    }

  上面的兩段代碼都是為了清晰易懂而精簡得不能再簡的,有很多地方很不完善。比如,沒考慮到如何從高亮顯示狀態復原;也沒有大小寫匹配等等。當然,掌握了原理之後相信這些都不會太難。 

    這兩種方法各有優缺點:
使用TextRange較輕量迅速,而且有一個特長,就是可以把跨標簽(Tag)的關鍵字挑出來。例如,有這麼一段HTML:


<b>Helb>lo World!

  先不管作者出於什麼目的讓Hel三個字母成為粗體,總之顯示在頁面上的是一句“Hello World!”。在我們希望高亮頁面中的“Hello”這個關鍵字時,如果用DOM分析的話,會得出含有“Hel”的節點和文本節點“lo World!”兩個節點,因此無法將其挑出來。而TextRange則能正確識別,將其設置為高亮。因此也可以說TextRange是只和文本有關,和HTML語法結構無關的對象。

  但是,TextRange也有其致命缺點,加亮容易,反向的話就很難。換句話說,去除高亮顯示的時候不能再用TextRange,而需要采用其他方法。

  而DOM方法則正好相反, 由於DOM的樹狀結構特性,雖然不能(或者很難)跨越Tag搜索關鍵字,但是去除高亮顯示並不繁瑣。

  Study Case 5:與腳本的互操作

  在Case 1當中,我們已經看到,Web頁面的HTML元素的事件,可以由Windows Form端來響應,可以在某種程度上看作是Web頁面調用WinForm;那麼反過來,WinForm除了可以直接訪問Web頁面的HTML元素之外,能否調用Web頁面裡的各種Script呢?

  首先是調用Web頁面的腳本中已經定義好的函數。假設HTML中有如下Javascript:


function DoAdd(a, b) {
    return a + b;
}

  那麼,我們要在WinForm調用它,只需如下代碼即可:


object oSum = webBrowser.Document.InvokeScript("DoAdd", new object[] { 1, 2 });
int sum = Convert.ToInt32(oSum);

  其次,如果我們想執行一段Web頁面中原本沒有的腳本,該怎麼做呢?這次.Net的類沒有提供,看來還要依靠COM了。IHTMLWindow2可以將任意的字符串作為腳本代碼來執行。


string scriptline01 = @"function ShowPageInfo() {";
string scriptline02 = @"     var numLinks = document.links.length; ";
string scriptline03 = @"     var numForms = document.forms.length; ";
string scriptline04 = @"     var numImages = document.images.length; ";
string scriptline05 = @"     var numScripts = document.scripts.length; ";
string scriptline06 = @"     alert('網頁的統計結果:\r\n鏈接數:' + numLinks + ";
string scriptline07 = @"        '\r\n表單數:' + numForms + ";
string scriptline08 = @"        '\r\n圖像數:' + numImages + ";
string scriptline09 = @"        '\r\n腳本數:' + numScripts);}";
string scriptline10 = @"ShowPageInfo();";

string strScript = scriptline01 + scriptline02 + scriptline03 + scriptline04 + scriptline05 +
                   scriptline06 + scriptline07 + scriptline08 + scriptline09 + scriptline10;

IHTMLWindow2 win = (IHTMLWindow2)webBrowser.Document.Window.DomWindow;
win.execScript(strScript, "Javascript");


From:http://tw.wingwit.com/Article/program/net/201311/12613.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.