c#

位置:IT落伍者 >> c# >> 浏览文章

WebBrowser页面与WinForm的交互


发布日期:2018年07月14日
 
WebBrowser页面与WinForm的交互
话说有了WebBrowser类终于不用自己手动封装SHDocVw的AxWebBrowser这个ActiveX控件了这个类如果仅仅作为一个和IE一模一样浏览器那就太没意思了(还不如直接用IE呢)那么无论我们是想做一个定制版IE还是希望利用HTML来做用户界面(指WinApp而非WebApp许多单机软件包括Windows的帮助支持中心都是HTML做的)都少不了Windows Form和包含在WebBrowser中的Web页面的交互本文将通过几个实际的例子初步介绍一下WinForm和WebBrowser所包含的Web页面之间的交互下面的代码假设你已经建立了一个Windows Form上面有一个WebBrowser名为webBrowserStudy 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中有如下Javascriptfunction 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);               

上一篇:利用.NET Framework命令行工具

下一篇:用VS2005开发XAML程序