您的位置:首页 > Web前端 > JavaScript

蛙蛙推荐:WebBrowser控件的高级用法,c#和Javascript交互及光标位置恢复

2008-10-14 16:38 369 查看
蛙蛙推荐:WebBrowser控件的高级用法,c#和Javascript交互及光标位置恢复

摘要:在做Winform应用的时候,有些效果不太好做,不像网页,用CSS和HTML能做出灰常漂亮的界面来,其实用WebBrowser可以让你的程序拥有两者的优势。这里介绍一个winform内嵌WebBrowser做的一个RSS浏览器及内嵌在Winform里的html编辑器的光标恢复问题。

  不知道大家有没有用过FeedDemon,它是一个不错的RSS订阅工具,左边的导航树是Winform的,右边的主区域是用WebBrowser来呈现的,而且在主区域点击某条RSS条目后左边的树节点相应的未读数目就会变化,点击左边的树选择“设置所有项目未已读”,右边主区域的RSS列表就会变为已读图标,这是一个典型的Winform和WebBrowser相结合的例子,既发挥了Winform能利用本地CPU计算能力,响应快的特点,又发挥了HTML编程容易,界面华丽的优势,要用winform写一个右边主区域效果的控件,估计得费不少力。

  我们先来看下这个RSS阅读器的核心功能,首先是利用http请求去获取rss内容,RSS是XML格式的,显示的话,就可以用XSLT来显示,这样可以做到内容和显示相分离,显示部分用WebBrowser来显示,然后呢WebBrowser里的javascript要可以调用Winform方法,Winform也要能调用Webbrowser控件里的javascript方法。

 RSS的XML格式大家都很熟悉了,随便找一段示例如下




RSS XML Sample
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:slash="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">

  <channel>

    <title>老农专注数据库编程</title>

    <link>http://www.5do8.com/</link>

    <description>老农如是想如是为</description>

    <copyright>copyright by 5do8</copyright>

    <item read="true">

      <title>我军步兵营所属炮连的98式120火箭筒

..</title>

      <description>我军步兵营所属炮连的98式120火箭筒

..</description>

      <link>/tableforum/App/view.php?bbsid=4&subid=1&fid=65544&tbid=8075</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:03:01 GMT

      </pubDate>

    </item>

    <item read="true">

      <title>温今早看望广州火车站80万滞留旅客:&quot;大..</title>

      <description>温今早看望广州火车站80万滞留旅客:&quot;大..</description>

      <link>/tableforum/App/view.php?bbsid=4&subid=2&fid=65638&tbid=2819</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:03:01 GMT

      </pubDate>

    </item>

    <item read="true">

      <title>温今早看望广州火车站80万滞留旅客:&quot;大..</title>

      <description>温今早看望广州火车站80万滞留旅客:&quot;大..</description>

      <link>/tableforum/App/view.php?bbsid=4&subid=3&fid=65639&tbid=6065</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:03:01 GMT

      </pubDate>

    </item>

    <item>

      <title>07最惹火十部国产片</title>

      <description>07最惹火十部国产片</description>

      <link>http://sh.sohu.com/7/1103/22/column216152269.shtml</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

    <item>

      <title>赚大钱取之有道火爆</title>

      <description>赚大钱取之有道火爆</description>

      <link>http://doc.go.sohu.com/200712/df66a3feef3110edd839666dcb3fc1de.php?url=http://vip.78.cn/zs/gbook_admin/getgo.php?id=452</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

    <item>

      <title>火!等一分钟</title>

      <description>火!等一分钟</description>

      <link>http://61.135.132.134/goto.php?aid=27&amp;pid=1153</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

    <item>

      <title>火车票预售期</title>

      <description>火车票预售期</description>

      <link>http://news.sogou.com/news?query=火车票预售??pid=01003102&p=01010302</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

    <item>

      <title>春运火车票</title>

      <description>春运火车票</description>

      <link>http://news.sogou.com/news?query=春运火车票&pid=01003102&p=01010302</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

    <item>

      <title>轻松购买火车票!</title>

      <description>轻松购买火车票!</description>

      <link>http://61.135.132.134/goto.php?aid=26&amp;pid=1175</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

    <item>

      <title>one-mall·火热招商</title>

      <description>one-mall·火热招商</description>

      <link>http://shop.focus.cn/shopview/450228.html</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

    <item>

      <title>六佰本火热招商进行中</title>

      <description>六佰本火热招商进行中</description>

      <link>http://shop.focus.cn/shopview/450319.html</link>

      <author></author>

      <pubDate>

        Wed, 30 Jan 2008 22:02:55 GMT

      </pubDate>

    </item>

  </channel>
</rss>

  我们还要再写个RSS来呈现这个RSS,就以列表的方式呈现就行了,然后我们设置简单的CSS,让已读的RSS条目显示为蓝色,未读的默认显示为黑色,然后点击每个条目的时候要把颜色改为已读的颜色,并且同时Winform。从上面的RSS定义可以看到我把前三个item元素加了一个read="true"的属性(为了演示目的我手工加的),我们用xslt的if语句来读取这个属性来动态设置RSS条目的样式。最后xslt还要定义一个让winform调用的javascript方法以供后面的演示。最终的XSLT如下,不太熟悉XSLT语法的可以参考我以前贴过的帖子。


<?xml version="1.0" encoding="utf-8" ?>


<xsl:stylesheet


     version="1.0"


     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">


  <xsl:output method="html" />


  <xsl:template match="rss/channel">


    <html>


      <head>


        <title>


          <xsl:value-of select="title" />


        </title>


        <SCRIPT LANGUAGE="JavaScript">


          function invokeWin(obj)


          {


          obj.childNodes[0].style.color='blue';


          window.external.winFunction(obj.childNodes[0].href);


          }


          function scriptFunc(str)


          {


          alert(str);


          }


        </SCRIPT>


        <style media="all" lang="en" type="text/css">


          body


          {


          background-color:#ccc;


          }


          .ChannelTitle


          {


          font-family:  Verdana;


          font-size:  11pt;


          font-weight:  bold;


          width:  500px;


          text-align:  center;


          }


          .PostDate


          {


          font-family:  Verdana;


          font-size:  7pt;


          padding-left:  15px;


          }


          A,A:visited


          {


          text-decoration:  none;


          color: #000


          }


          A:link


          {


          text-decoration:  none;


          }


          A:hover


          {


          text-decoration:  underline;


          }


        </style>


      </head>


      <body>


        <xsl:apply-templates select="title" />


        <ol>


          <xsl:apply-templates select="item" />


        </ol>


        <div align="center">© 2008WawaSoft 2008</div>


      </body>


    </html>


  </xsl:template>


  <xsl:template match="title">


    <div class="ChannelTitle">


      <xsl:value-of select="text()" />


    </div>


    <br />


  </xsl:template>


  <xsl:template match="item">


    <li>


      <span onclick="invokeWin(this)">


        <a TARGET="_blank" href="{link}">


          <xsl:if test="@read='true'">


            <xsl:attribute name="style">color:blue;</xsl:attribute>


          </xsl:if>


          <xsl:value-of select="title" />


        </a>


      </span>


      <span class="PostDate">


        <xsl:value-of select="pubDate" />


      </span>


    </li>


  </xsl:template>


</xsl:stylesheet>



程序里面呢,我们得让WebBrowser来显示这个RSS,代码如下

 

private void FWBTest_Load(object sender, EventArgs e)

{

    wb1.AllowWebBrowserDrop = false;

    //wb1.IsWebBrowserContextMenuEnabled = false;
    wb1.WebBrowserShortcutsEnabled = false;

    wb1.ObjectForScripting = this;

    wb1.ScriptErrorsSuppressed = true;

    try

    {

        XslCompiledTransform xslt = new XslCompiledTransform();

        xslt.Load("rss.xslt");

        string HTMLoutput;

        using (StringWriter writer = new StringWriter())

        {

            xslt.Transform("rss.xml", null, writer);

            HTMLoutput = writer.ToString();

        }

        wb1.DocumentText = HTMLoutput;

    }

    catch (XsltException xsle)

    {

        Console.WriteLine("样式表中有错。");

    }

    catch (XmlException xmle)

    {

        Console.WriteLine("加载样式表时出现分析错误。");

    }

    catch (Exception ex)

    {

        Console.WriteLine(ex);

    }

}
 

都是WebBrowser和.net的Xml对象的基本用法。

和javascript交互也很简单,以下分别是让javascript调用的方法和用c#调用javascript方法的代码。

public void winFunction(string str)

{

    toolStripStatusLabel1.Text = string.Format("脚本执行方法:{0}", str);

}

private void toolStripButton1_Click(object sender, EventArgs e)

{

    wb1.Document.InvokeScript("scriptFunc",

    new String[] { "这是winform调用脚本方法" });

}

关键点就是javascript代码的下面这句

window.external.winFunction(obj.childNodes[0].href);

以及c#代码下面这句

wb1.Document.InvokeScript("scriptFunc",new String[] { "这是winform调用脚本方法" });

RSS这部分演示完毕了。

winform下的富文本编辑器控件不太多,一般有两种途径,一种是扩展RichTextBox,要去研究rtf格式,mime协议等,一种是用WebBrowser控件,并把designMode设置为on,然后就是和网页的html编辑器一样,调用一些ExecCommand方法来编辑格式等,这里介绍后者,因为后者实现简单,而且html格式可以直接按原格式贴到互联网上,通用性比较好,这里不具体说一些编辑命令如何实现,这些可以参考文后的参考链接,这里只讨论如何让你的HTML文档关闭后再加载恢复上次编辑的光标位置的功能。

这里需要用一些mshtml对象,所以要在项目里引用Microsoft.mshtml,引用位置默认应该是C:/Program Files/Microsoft.NET/Primary Interop Assemblies/Microsoft.mshtml.dll,我不知道这个组件是不是新安装的windows xp,2003都有,如果没有的话可以把这个dll放到你项目的bin目录下一起发布。

大致原理是这样的,在关闭当前编辑文档的时候获取当前光标的位置,也就是当前选择的元素(虽然doc.selection有可能是个图片,而不是TextRange,但我们只考虑textRange的情况,大家可以写代码过滤掉其它情况,如果当前编辑点不是文本类型,就不考虑恢复光标位置了,呵呵。)。然后给当前选择元素后面插入一个小的span元素,作为一个标识符,最后再把文档的Body的InnerHTML保存到数据库里。下次从数据库里读取保存的html文本,先设置到当前WebBrowser的Body的innerHTML属性,然后通过getElementById方法找上次插入的标识符span,找到这个span就相当于找到了上次的光标位置,最后用body元素createTextRange后调用其moveToElementText方法把光标编辑位置移动到找到的位置。

相应代码如下


private void NewDoc()






{


    wb1.Navigate("about:blank");


    IHTMLDocument2 doc = wb1.Document.DomDocument as IHTMLDocument2;


    doc.designMode = "On";


}




private void SaveDoc()






{


    if (wb1.Document != null)




    

{


        IHTMLDocument2 doc = wb1.Document.DomDocument as IHTMLDocument2;


        HTMLDocumentClass documentClass = wb1.Document.DomDocument as HTMLDocumentClass;


        IHTMLDOMNode caret_pos = (IHTMLDOMNode)documentClass.getElementById("caret_pos");


        if (caret_pos != null) caret_pos.removeNode(true);


        IHTMLTxtRange range = doc.selection.createRange() as IHTMLTxtRange;


        range.pasteHTML(caretHtml);


        range.collapse(true);


        _text = doc.body.innerHTML;


        doc.body.innerHTML = "";


    }


}




private void LoadDoc()






{


    if (!string.IsNullOrEmpty(_text))




    

{


        IHTMLDocument2 doc = wb1.Document.DomDocument as IHTMLDocument2;


        doc.body.innerHTML = _text;


        IHTMLBodyElement bodyElement = doc.body as IHTMLBodyElement;


        if (bodyElement != null)




        

{


            IHTMLTxtRange range = bodyElement.createTextRange();


            HTMLDocumentClass documentClass = wb1.Document.DomDocument as HTMLDocumentClass;


            IHTMLElement caret_pos = documentClass.getElementById("caret_pos");


            if (caret_pos != null)




            

{


                range.moveToElementText(caret_pos);


                range.select();


            }


        }


        _text = "";


    }


}





注:代码写的不严谨,只用于演示目的,请勿用于生产环境。

关于Winform html编辑器的参考链接
http://windowsclient.net/articles/htmleditor.aspx

http://www.codeproject.com/cs/miscctrl/editor_in_windows_forms.asp

http://www.codeproject.com/KB/IP/WYSIWYG_netHTML2.aspx

关于恢复光标位置的参考链接

设置光标位置的问题:SetDocumentHTML(html) 之后, SetCaretPos(curpos) 为何失效?

http://topic.csdn.net/t/20050729/22/4177529.html

HTML可视化编辑器中IE丢失光标位置的问题。

http://hi.baidu.com/jindw/blog/item/8c3e928ba1f04dd0fc1f10d2.html

用土办法记忆可编辑div 内的光标位置- 极湖- by OU(链接不可用,请从google缓存里查看)

http://my.opera.com/jlake/blog/2008/05/05/div

How to RemoveChild from HtmlElement from C#
http://bytes.com/forum/thread453742.html

本文源码下载地址如下

 WebBrowserDemo.zip
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息