2013年9月11日 星期三

[轉貼] C# WebBrowser實現網頁自動填表

出處:http://www.sosuo8.com/article-2011/webbrowser-control-form-automatically-fill-and-submit.htm


話說有了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 1:用WinForm的Event Handler響應Web頁面的事件
現在有這樣一個Windows Application,它的界面上只有一個WebBrowser,顯示一個本地的HTML文件作為界面。現在的問題是,所有邏輯都可以放在HTML文件裡,唯獨「關閉」按鈕遇到了困難——通常,Web頁面是沒有辦法直接控制瀏覽器的,更不用說結束這個WinForm程序了。
但是,在.Net 2.0當中,「由Windows Form響應Web頁面的事件」已經成為了現實。
在.Net 2.0中,整個HTML文檔以及其包含的各個HTML元素,都和一個個HtmlDocument、HtmlElement之類的.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。
很簡單吧?那麼稍稍高級一點的——我們都知道一個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元素的同時,提供了這兩個方法以調用元素特有的功能。
※關於表單的提交,的確還有另一種方法就是獲取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();
            searchRange.collapse(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("已搜索到文檔結尾。");
        }
    }
}

沒有留言:

張貼留言