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

WebBrowser頁面與WinForm的交互

2013-11-13 10:06:53  來源: .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源代碼如下 body> html>   那麼找出該按鈕並為之添加Event Handler的代碼如下 HtmlDocument htmlDoc = webBrowserDocument; HtmlElement btnElement = htmlDocAll[btnClose]; if (btnElement != null) { btnElementclick += new HtmlElementEventHandler(HtmlBtnClose_Click); }   其中HtmlBtnClose_Click是按下Web按鈕時的Event Handler      很簡單吧?那麼稍稍高級一點的——我們都知道一個HTML元素可能有很多各種各樣的事件而HtmlElement這個類只給出最常用共通的幾個那麼如何響應其他事件呢?這也很簡單只需要調用HtmlElement的AttachEventHandler就可以了       btnElementAttachEventHandler(onclick new EventHandler(HtmlBtnClose_Click)); //這一句等價於上面的btnElementclick += new HtmlElementEventHandler(HtmlBtnClose_Click);   把onclick換成其他事件的名字就可以了例如 formElementAttachEventHandler(onsubmit new EventHandler(HtmlForm_Submit));   Study Case 表單(form)的自動填寫和提交   要使我們的WebBrowser具有自動填表甚至自動提交的功能並不困難   假設有一個最簡單的登錄頁面輸入用戶名密碼登錄按鈕即可登錄已知用戶名輸入框的id(或Name下同)是username密碼輸入框的id是password登錄按鈕的id是submitbutton那麼我們只需要在webBrowser的DocumentCompleted事件中使用下面的代碼即可 HtmlElement btnSubmit = webBrowserDocumentAll[submitbutton]; HtmlElement tbUserid = webBrowserDocumentAll[username]; HtmlElement tbPasswd = webBrowserDocumentAll[password]; if (tbUserid == null || tbPasswd == null || btnSubmit == null) return; tbUseridSetAttribute(value smalldust); tbPasswdSetAttribute(value ); btnSubmitInvokeMember(click);   這裡我們用SetAttribute來設置文本框的value屬性用InvokeMember來調用了按鈕的click方法因為不同的Html元素其擁有的屬性和方法也不盡相同所以Net 提供了統一的HtmlElement來概括各種Html元素的同時提供了這兩個方法以調用元素特有的功能關於各種Html元素的屬性和方法一覽可以查閱MSDN的DHTML Reference ※關於表單的提交的確還有另一種方法就是獲取form元素而不是button並用form元素的submit方法 HtmlElement formLogin = webBrowserDocumentForms[loginForm]; //…… formLoginInvokeMember(submit);   本文之所以沒有推薦這種方法是因為現在的網頁很多都在submit按鈕上添加onclick事件以對提交的內容做最基本的驗證如果直接使用form的submit方法這些驗證代碼就得不到執行有可能會引起錯誤   Study Case 查找並選擇文本   這次我們希望實現一個和IE一模一樣的查找功能以對Web頁面內的文字進行查找   文本查找要借助於TextRange對象的findText方法但是Net裡並沒有這個對象這是因為Net 提供的HtmlDocumentHtmlWindowHtmlElement等類只不過是對原有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對象 IHTMLDocument document = (IHTMLDocument)webBrowserDocumentDomDocument; string keyword = txtKeywordTextTrim(); if (keyword == ) return; // IE的查找邏輯就是如果有選區就從當前選區開頭+字符處開始查找沒有的話就從頁面最初開始查找 // 這個邏輯其實是有點不大恰當的我們這裡不用管和IE一致即可 if (documentselectiontypeToLower() != none) { searchRange = (IHTMLTxtRange)documentselectioncreateRange(); llapse(true); searchRangemoveStart(character ); } else { IHTMLBodyElement body = (IHTMLBodyElement)documentbody; searchRange = (IHTMLTxtRange)bodycreateTextRange(); } // 如果找到了就選取(高亮顯示)該關鍵字否則彈出消息 if (searchRangefindText(keyword )) { searchRangeselect(); } else { MessageBoxShow(已搜索到文檔結尾); } } }   到此為止簡單的查找就搞定了至於替換功能看了下一個例子我相信你就可以觸類旁通輕松搞定了   Study Case 高亮顯示   上一個例子中我們學會了查找文本——究跟到底對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 = webBrowserDocument; string keyword = txtKeywordTextTrim(); if (keyword == ) return; object oTextRange = htmlDocBodyInvokeMember(createTextRange); mshtmlIHTMLTxtRange txtrange = oTextRange as mshtmlIHTMLTxtRange; while (txtrangefindText(keyword )) { try { txtrangepasteHTML(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)webBrowserDocumentDomDocument; IHTMLDOMNode bodyNode = (IHTMLDOMNode)webBrowserDocumentBodyDomElement; string keyword = txtKeywordTextTrim(); if (keyword == ) return; HilightText(document bodyNode keyword); } private void HilightText(HTMLDocument document IHTMLDOMNode node string keyword) { // nodeType = text節點 if (nodenodeType == ) { string nodeText = nodenodeValueToString(); // 如果找到了關鍵字 if (nodeTextContains(keyword)) { IHTMLDOMNode parentNode = nodeparentNode; // 將關鍵字作為分隔符將文本分離並逐個添加到原text節點的父節點 string[] result = nodeTextSplit(new string[] { keyword } StringSplitOptionsNone); for (int i = ; i < resultLength ; i++) { if (result[i] != ) { IHTMLDOMNode txtNode = documentcreateTextNode(result[i]); parentNodeinsertBefore(txtNode node); } IHTMLDOMNode orgNode = documentcreateTextNode(keyword); IHTMLDOMNode hilightedNode = (IHTMLDOMNode)documentcreateElement(SPAN); IHTMLStyle style = ((IHTMLElement)hilightedNode)style; lor = black; stylebackgroundColor = yellow; hilightedNodeappendChild(orgNode); parentNodeinsertBefore(hilightedNode node); } if (result[resultLength ] != ) { IHTMLDOMNode postNode = documentcreateTextNode(result[resultLength ]); parentNodeinsertBefore(postNode node); } parentNoderemoveChild(node); } // End of nodeTextContains(keyword) } else { // 如果不是text節點則遞歸搜索其子節點 IHTMLDOMChildrenCollection childNodes = nodechildNodes as IHTMLDOMChildrenCollection; foreach (IHTMLDOMNode n in childNodes) { HilightText(document n keyword); } } } }   上面的兩段代碼都是為了清晰易懂而精簡得不能再簡的有很多地方很不完善比如沒考慮到如何從高亮顯示狀態復原也沒有大小寫匹配等等當然掌握了原理之後相信這些都不會太難   這兩種方法各有優缺點   使用TextRange較輕量迅速而且有一個特長就是可以把跨標簽(Tag)的關鍵字挑出來例如有這麼一段HTML Helb>lo World!   先不管作者出於什麼目的讓Hel三個字母成為粗體總之顯示在頁面上的是一句Hello World!在我們希望高亮頁面中的Hello這個關鍵字時如果用DOM分析的話會得出含有Hel的節點和文本節點lo World!兩個節點因此無法將其挑出來而TextRange則能正確識別將其設置為高亮因此也可以說TextRange是只和文本有關和HTML語法結構無關的對象   但是TextRange也有其致命缺點加亮容易反向的話就很難換句話說去除高亮顯示的時候不能再用TextRange而需要采用其他方法   而DOM方法則正好相反 由於DOM的樹狀結構特性雖然不能(或者很難)跨越Tag搜索關鍵字但是去除高亮顯示並不繁瑣   Study Case 與腳本的互操作   在Case 當中我們已經看到Web頁面的HTML元素的事件可以由Windows Form端來響應可以在某種程度上看作是Web頁面調用WinForm那麼反過來WinForm除了可以直接訪問Web頁面的HTML元素之外能否調用Web頁面裡的各種Script呢?   首先是調用Web頁面的腳本中已經定義好的函數假設HTML中有如下Javascript function DoAdd(a b) { return a + b; }   那麼我們要在WinForm調用它只需如下代碼即可 object oSum = webBrowserDocumentInvokeScript(DoAdd new object[] { }); int sum = ConvertToInt(oSum);   其次如果我們想執行一段Web頁面中原本沒有的腳本該怎麼做呢?這次Net的類沒有提供看來還要依靠COM了IHTMLWindow可以將任意的字符串作為腳本代碼來執行 string scriptline = @function ShowPageInfo() {; string scriptline = @ var numLinks = documentlinkslength; ; string scriptline = @ var numForms = documentformslength; ; string scriptline = @ var numImages = documentimageslength; ; string scriptline = @ var numScripts = documentscriptslength; ; string scriptline = @ alert(網頁的統計結果\r\n鏈接數 + numLinks + ; string scriptline = @ \r\n表單數 + numForms + ; string scriptline = @ \r\n圖像數 + numImages + ; string scriptline = @ \r\n腳本數 + numScripts);}; string scriptline = @ShowPageInfo();; string strScript = scriptline + scriptline + scriptline + scriptline + scriptline + scriptline + scriptline + scriptline + scriptline + scriptline; IHTMLWindow win = (IHTMLWindow)webBrowserDocumentWindowDomWindow; winexecScript(strScript Javascript);
From:http://tw.wingwit.com/Article/program/net/201311/12600.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.