今天有個同事問了一下我,怎么在winform里面打開網頁啊?我們都是基於B/S的開發,很少接觸winform,所以我當時就懵了,實在不知道怎么回答,所以索性說不知道。但是我又想了想,這個應該是個很簡單的功能,趁着今天工作不是很忙,我就研究一下吧。
首先,新建一個winform項目,我在想,如果想要實現打開網頁功能的話,應該會有一個控件什么之類的吧?查了工具欄,真的有一個名叫 WebBrowser的家伙,應該就是這貨沒錯了。在網上查了它的資料更加堅定了我的判斷,二話不說,拖進Form里。接着,就是要顯示一個網頁了,要怎 么實現呢?繼續查看WebBrowser都有啥屬性和方法:
方法
|
說明
|
GoBack | 相當於IE的“后退”按鈕,使你在當前歷史列表中后退一項 |
GoForward | 相當於IE的“前進”按鈕,使你在當前歷史列表中前進一項 |
GoHome | 相當於IE的“主頁”按鈕,連接用戶默認的主頁 |
GoSearch | 相當於IE的“搜索”按鈕,連接用戶默認的搜索頁面 |
Navigate | 連接到指定的URL |
Refresh | 刷新當前頁面 |
Refresh2 | 同上,只是可以指定刷新級別,所指定的刷新級別的值來自RefreshConstants枚舉表, 該表定義在ExDisp.h中,可以指定的不同值如下: REFRESH_NORMAL 執行簡單的刷新,不將HTTP pragma: no-cache頭發送給服務器 REFRESH_IFEXPIRED 只有在網頁過期后才進行簡單的刷新 REFRESH_CONTINUE 僅作內部使用。在MSDN里寫着DO NOT USE! 請勿使用 REFRESH_COMPLETELY 將包含pragma: no-cache頭的請求發送到服務器 |
Stop | 相當於IE的“停止”按鈕,停止當前頁面及其內容的載入 |
屬性
|
說明
|
Application | 如果該對象有效,則返回掌管WebBrowser控件的應用程序實現的自動化對象(IDispatch)。如果在宿主對象中自動化對象無效,這個程序將返回WebBrowser 控件的自動化對象 |
Parent | 返回WebBrowser控件的父自動化對象,通常是一個容器,例如是宿主或IE窗口 |
Container | 返回WebBrowser控件容器的自動化對象。通常該值與Parent屬性返回的值相同 |
Document | 為活動的文檔返回自動化對象。如果HTML當前正被顯示在WebBrowser中,則 Document屬性提供對DHTML Object Model的訪問途徑 |
TopLevelContainer | 返回一個Boolean值,表明IE是否是WebBrowser控件頂層容器,是就返回true |
Type | 返回已被WebBrowser控件加載的對象的類型。例如:如果加載.doc文件,就會返 回Microsoft Word Document |
Left | 返回或設置WebBrowser控件窗口的內部左邊與容器窗口左邊的距離 |
Top | 返回或設置WebBrowser控件窗口的內部左邊與容器窗口頂邊的距離 |
Width | 返回或設置WebBrowser窗口的寬度,以像素為單位 |
Height | 返回或設置WebBrowser窗口的高度,以像素為單位 |
LocationName | 返回一個字符串,該字符串包含着WebBrowser當前顯示的資源的名稱,如果資源 是網頁就是網頁的標題;如果是文件或文件夾,就是文件或文件夾的名稱 |
LocationURL | 返回WebBrowser當前正在顯示的資源的URL |
Busy | 返回一個Boolean值,說明WebBrowser當前是否正在加載URL,如果返回true 就可以使用stop方法來撤銷正在執行的訪問操作 |
事件 | 說明 |
---|---|
BeforeNavigate2 | 導航發生前激發,刷新時不激發 |
CommandStateChange | 當命令的激活狀態改變時激發。它表明何時激活或關閉Back和Forward 菜單項或按鈕 |
DocumentComplete | 當整個文檔完成是激發,刷新頁面不激發 |
DownloadBegin | 當某項下載操作已經開始后激發,刷新也可激發此事件 |
DownloadComplete | 當某項下載操作已經完成后激發,刷新也可激發此事件 |
NavigateComplete2 | 導航完成后激發,刷新時不激發 |
NewWindow2 | 在創建新窗口以前激發 |
OnFullScreen | 當FullScreen屬性改變時激發。該事件采用VARIENT_BOOL的一個輸 入參數來指示IE是全屏顯示方式(VARIENT_TRUE)還是普通顯示方式(VARIENT_FALSE) |
OnMenuBar | 改變MenuBar的屬性時激發,標示參數是VARIENT_BOOL類型的。 VARIANT_TRUE是可見,VARIANT_ FALSE是隱藏 |
OnQuit | 無論是用戶關閉瀏覽器還是開發者調用Quit方法,當IE退出時就會激發 |
OnStatusBar | 與OnMenuBar調用方法相同,標示狀態欄是否可見。 |
OnToolBar | 調用方法同上,標示工具欄是否可見。 |
OnVisible | 控制窗口的可見或隱藏,也使用一個VARIENT_BOOL類型的參數 |
StatusTextChange | 如果要改變狀態欄中的文字,這個事件就會被激發,但它並不理會程序是否有狀態欄 |
TitleChange | Title有效或改變時激發 |
看了以上信息后,Navigate這個方法最靠譜,所以在Form1_Load中加入了一句代碼:
webBrowser1.Navigate("http://www.baidu.com/");
運行后百度的頁面出來了,搞定!原來如此簡單!
這么一句就搞定了,我開始不滿足於現狀,我想查看一下生成baidu的網頁源代碼,有一個屬性是:DocumentText,我想應該是這個,於是我在Form1_Load加了這么一句:
this.textBox1.Text = webBrowser1.DocumentText;
運行后,發現textbox里啥都沒顯示,調試發現webBrowser1.DocumentText居然是空值,難道是我 DocumentText不是顯示頁面內容的屬性?不對,我再加入webBrowser1.Document來調試,發現居然是個null值,一定是哪里 弄錯了!
我仔細檢查了一下,原來是我看事件不仔細,webBrowser1.DocumentText這個必須放在webBrowser1_DocumentCompleted事件中,等頁面加載完成后,才能獲取到網頁的值,再試一下,果然成功了!
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) { this.textBox1.Text = webBrowser1.DocumentText; }
不過……為什么會有亂碼啊?!
不行,我要把亂碼去掉,默認是utf-8的頁面,我只要把它轉換成gb2312即可,所以就不能直接webBrowser1.DocumentText,我打算采用StreamReader來轉換好一些:
StreamReader getReader = new StreamReader(this.webBrowser1.DocumentStream, Encoding.GetEncoding("gb2312")); this.textBox1.Text = getReader.ReadToEnd();
OK,兩句就搞定!注意哦,我這里用的是this.webBrowser1.DocumentStream
我又突發奇想,如果我只想要獲取網頁中的圖片地址該怎么辦咧?大概的思路就是從生成的網頁源代碼里找到img標簽,並從src里取出來唄,於是有了以下代碼:
StreamReader getReader = new StreamReader(this.webBrowser1.DocumentStream, Encoding.GetEncoding("gb2312")); ArrayList list = new ArrayList(); string strRegex = "<img (.|\n)+?>"; Regex regex = new Regex(strRegex, RegexOptions.IgnoreCase); MatchCollection match = regex.Matches(getReader.ReadToEnd()); int startp, endp; for (int i = 0; i < match.Count; i++) { bool flag = false; string Img = match[i].ToString(); startp = Img.ToLower().IndexOf("src=\""); if (startp != -1) { startp = startp + 5; endp = Img.ToLower().IndexOf("\"", startp + 1); Img = Img.Substring(startp, endp - startp); foreach (string str in list) { if (Img == "str" || Img == "/" || Img == "") { flag = true; break; } } if (!flag) { this.textBox1.Text += Img.ToString() + "\r\n"; } } }
輸出以下結果:
http://www.baidu.com/img/shouye_b5486898c692066bd2cbaeda86d74448.gif
http://www.baidu.com/cache/global/img/gs.gif
雖然是成功取出了圖片地址,但是感覺總是寫這么多代碼,有點不妥,這樣一種取圖片的功能,我想應該是很多人都會有這種需求,應該有啥控件或插件之類的吧?再網上搜了一下,果然讓我找到了一個比較好用的——HtmlAgilityPack
把HtmlAgilityPack.dll引入項目后,只要編寫如下代碼就能實現如上的功能:
string strImg = ""; HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.LoadHtml(getReader.ReadToEnd()); foreach (var img in doc.DocumentNode.SelectNodes("//img")) { strImg += img.Attributes["src"].Value.ToString() + "\r\n"; }
this.textBox1.Text = strImg;
太強大了,我想HtmlAgilityPack這貨的能力應該不僅僅如此,比如還可以用它來去腳本,去樣式,和去注釋等:
foreach (var script in doc.DocumentNode.Descendants("script").ToArray()) { script.Remove(); } foreach (var style in doc.DocumentNode.Descendants("style").ToArray()) { style.Remove(); } foreach (var comment in doc.DocumentNode.SelectNodes("//comment()").ToArray()) { comment.Remove(); }
就是如此簡單,寫更少的代碼,做更多的事!
好了,今天就研究到這吧,不然隨着深入,我會越陷越深,工作都顧不上了,就此打住吧。對於HtmlAgilityPack我想以后有時間,我會詳細研究它的。
總結一下:winform這東西,我幾百年都沒去碰過它,不過既然碰上了,就搗騰一番,寫篇文章來記錄一下當時我研究了啥,為啥研究,怎么研 究,以免以后忘了。對於新事物和新問題,我都是這么去解決的,不知道有沒有人和我一樣,躺着也中槍?以后還有好多東西要去學習和研究,努力,加油!正能量 爆發!!嘎嘎嘎!!!
工作去……