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;
          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)" >
        
< 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值