zoukankan      html  css  js  c++  java
  • 蛙蛙推荐:WebBrowser控件的高级用法,c#和Javascript交互及光标位置恢复

    蛙蛙推荐: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

      我们还要再写个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;
                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)">
            
    <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

  • 相关阅读:
    虚拟机下修改ip配置
    php cli 下 php.ini 配置
    centos 默认php 版本太低移到高版本的办法
    liux 防火墙以及开关
    [POI2006]OKR-Periods of Words(KMP)
    KMP
    [NOI1999]生日蛋糕(搜索)
    [HAOI2008]糖果传递
    [HEOI2015]兔子与樱花(贪心)
    [POJ3694]Network(Tarjan,LCA)
  • 原文地址:https://www.cnblogs.com/onlytiancai/p/webbrowser.html
Copyright © 2011-2022 走看看