博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何:在 DHTML 代码和客户端应用程序代码之间实现双向通信
阅读量:5931 次
发布时间:2019-06-19

本文共 9417 字,大约阅读时间需要 31 分钟。

https://msdn.microsoft.com/zh-cn/library/a0746166

可以使用  控件向 Windows 窗体客户端应用程序添加现有的动态 HTML (DHTML) Web 应用程序代码。 在投入大量的开发时间创建基于 DHTML 的控件,并希望利用丰富的 Windows 窗体用户界面功能而无需重写现有的代码时,该控件十分有用。

使用  控件,可以通过  和  属性在客户端应用程序代码和网页脚本代码之间实现双向通信。 此外,可以对  控件进行配置,使 Web 控件可以与应用程序窗体上的其他控件进行无缝整合,从而隐藏其 DHTML 实现。 若要使控件无缝整合,请对所显示页的格式进行设置,使其背景颜色和视觉样式与窗体的其余部分匹配,然后使用 、 和  属性禁用标准浏览器功能。

在 Windows 窗体应用程序中嵌入 DHTML

  1. 将  控件的  属性设置为 false,以防止  控件打开拖放到其上的文件。

    C#
     
    webBrowser1.AllowWebBrowserDrop = false;
  2. 将该控件的  属性设置为 false,以防止  控件在用户右击它时显示其快捷菜单。

    C#
     
    webBrowser1.IsWebBrowserContextMenuEnabled = false;
  3. 将该控件的  属性设置为 false,以防止  控件响应快捷键。

    C#
     
    webBrowser1.WebBrowserShortcutsEnabled = false;
  4. 在窗体的构造函数或  事件处理程序中设置  属性。

    下面的代码将窗体类自身用于脚本对象。

    说明 说明

    组件对象模型 (COM) 必须能够访问脚本对象。 若要使窗体对 COM 可见,请将  特性添加到窗体类中。

    C#
     
    webBrowser1.ObjectForScripting = this;
  5. 在应用程序代码中实现脚本代码将使用的公共属性或方法。

    例如,如果要使用脚本对象的窗体类,请向窗体类添加以下代码。

    C#
     
    public void Test(String message){    MessageBox.Show(message, "client code");}
  6. 使用脚本代码中的 window.external 对象访问指定对象的公共属性和方法。

    下面的 HTML 代码演示如何通过单击按钮对脚本对象调用方法。 将此代码复制到 HTML 文档的 BODY 元素中,该文档使用控件的  方法加载或被分配给控件的 属性。

     
     
  7. 在脚本代码中实现应用程序代码将使用的函数。

    下面的 HTML SCRIPT 元素提供了一个示例函数。 将此代码复制到 HTML 文档的 HEAD 元素中,该文档使用控件的  方法加载或被分配到控件的  属性。

     
     
  8. 使用  属性从客户端应用程序代码访问脚本代码。

    例如,将下面的代码添加到按钮  的事件处理程序中。

    C#
     
    webBrowser1.Document.InvokeScript("test",    new String[] { "called from client code" });
  9. 调试完 DHTML 之后,将该控件的  属性设置为 true,以防止  控件显示脚本代码问题的错误信息。

    C#
     
    // Uncomment the following line when you are finished debugging.//webBrowser1.ScriptErrorsSuppressed = true;
 

下面的完整代码示例提供了一个演示应用程序,可用于理解此功能。 HTML 代码通过  属性加载到  控件中,而不是从单独的 HTML 文件加载。

C#
 
using System;using System.Windows.Forms;using System.Security.Permissions;[PermissionSet(SecurityAction.Demand, Name="FullTrust")][System.Runtime.InteropServices.ComVisibleAttribute(true)]public class Form1 : Form{    private WebBrowser webBrowser1 = new WebBrowser();    private Button button1 = new Button();    [STAThread]    public static void Main()    {        Application.EnableVisualStyles();        Application.Run(new Form1());    }    public Form1()    {        button1.Text = "call script code from client code";        button1.Dock = DockStyle.Top;        button1.Click += new EventHandler(button1_Click);        webBrowser1.Dock = DockStyle.Fill;        Controls.Add(webBrowser1);        Controls.Add(button1);        Load += new EventHandler(Form1_Load);    }    private void Form1_Load(object sender, EventArgs e)    {        webBrowser1.AllowWebBrowserDrop = false;        webBrowser1.IsWebBrowserContextMenuEnabled = false;        webBrowser1.WebBrowserShortcutsEnabled = false;        webBrowser1.ObjectForScripting = this;        // Uncomment the following line when you are finished debugging.        //webBrowser1.ScriptErrorsSuppressed = true;        webBrowser1.DocumentText =            "" +            "";    }    public void Test(String message)    {        MessageBox.Show(message, "client code");    }    private void button1_Click(object sender, EventArgs e)    {        webBrowser1.Document.InvokeScript("test",            new String[] { "called from client code" });    }}------------------------------------------------------------- http://www.cnblogs.com/liuzhendong/archive/2012/03/21/2409159.html

最近工作需要,学习了一下winform内嵌webbrowser控件,然后与htm页面中的javascript交互调用的技术,因此有了这篇心得。

总的来说,javascript与winform的code互相调用,和web开发中javascript与服务器端代码通过ajax互相调用有类似之处。

下面就用三个例子来说明:

 

一.将WebBrowser控件放置在winform中,然后,写一个Page1.htm,内容如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
 <head>
  <title></title>
        <script type="text/javascript">
            function test(message) 
            {
                alert(message); 
            }
        </script>
 </head>
 <body>
     <button οnclick="test('test called from script code')">Button</button>
 </body>
</html>

我将此Page1.htm显示在WebBrowser中看看,这个不难写,在winform中加上下面一句即可。

webBrowser1.Url= new Uri("C:\\workspace\\WindowsFormsApp\\WindowsFormsApp\\Page1.htm");

然后运行,在winform中的webbrowser显示出来这个htm了,点按钮调用javascript函数,弹出alert提示,一切都很正常,没什么稀奇。

 

二.如果我把javascript中的函数挪到winform的cs代码里,htm页面还能调用的到吗?

这有点ajax的味道了,在客户端的javascript里如何调用webpage.aspx.cs里的代码,在ajaxpro那时候,是需要在webpage.aspx.cs的代码里注册一下本页供ajax使用,在函数前也要声明一下是ajax函数的。

再说回来,如果想调用winform中的代码,也类似的,要给winform设置一下ComVisibleAttribute(true), 并给webbrowser控件设置一下webBrowser1.ObjectForScripting属性。

webBrowser1.Url= new Uri("C:\\workspace\\WindowsFormsApp\\WindowsFormsApp\\Page1.htm");
webBrowser1.ObjectForScripting = this;

其实,如果做的好,可以把这些代码专门归入一个类中,方便管理,这里就变为:webBrowser1.ObjectForScripting = new 某类()了;

 

然后,再在winform里写一个函数。

        public void Test(String message)
        {
            MessageBox.Show(message, "client code");
        }

最后,htm里调用时要用window.external前缀一下Test方法名。

<button οnclick="window.external.Test('test called from windows code')">Button</button>

 

然后再运行,就发现,htm里的onclick事件,居然能调用winform里的code了,真是神奇!

 

完整winform代码如下:

using System;
using System.Windows.Forms;
using System.Security.Permissions;
  
namespace WindowsFormsApp
{
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    [System.Runtime.InteropServices.ComVisibleAttribute(true)]
    public partial class Form2 : Form
    {
        private WebBrowser webBrowser1 = new WebBrowser();

        public Form2()

        {
            InitializeComponent();

            button1.Text = "call script code from client code";

            button1.Dock = DockStyle.Top;
            button1.Click += new EventHandler(button1_Click);
            webBrowser1.Dock = DockStyle.Fill;
            Controls.Add(webBrowser1);
            Load += new EventHandler(Form2_Load);

        }

        private void Form2_Load(object sender, EventArgs e)

        {
            webBrowser1.AllowWebBrowserDrop = false;
            webBrowser1.IsWebBrowserContextMenuEnabled = false;
            webBrowser1.WebBrowserShortcutsEnabled = false;
            webBrowser1.ObjectForScripting = this;
            webBrowser1.Url= new Uri("C:\\workspace\\WindowsFormsApp\\WindowsFormsApp\\Page1.htm");
        }

        public void Test(String message)

        {
            MessageBox.Show(message, "client code");
        }      
    }
}

总结一下,关键的webBrowser1.ObjectForScripting属性,ComVisibleAttribute(true)和window.external。

msdn说webBrowser1.ObjectForScripting属性的作用是:获取或设置一个对象,该对象可由显示在 WebBrowser 控件中的网页所包含的脚本代码访问。使用该属性可以启用 WebBrowser 控件承载的网页与包含 WebBrowser 控件的应用程序之间的通信。使用该属性可以将动态 HTML (DHTML) 代码与客户端应用程序代码集成在一起。为该属性指定的对象可作为 window.external 对象(用于主机访问的内置 DOM 对象)用于网页脚本。

 

可以将此属性设置为希望其公共属性和方法可用于脚本代码的任何 COM 可见的对象。可以通过使用 ComVisibleAttribute 对类进行标记使其成为 COM 可见的类。

这一步也至关重要,如果不设置ComVisibleAttribute(true),那这个程序就不能加载显示htm页面,因为htm里用了window.external.Test()方法,该方法所在的类如果不ComVisible,就无法访问到了。反过来,如果设置了ComVisible,却不设置webBrowser1.ObjectForScripting属性,那代码执行时会报错:window.external无效或找不到对象。

 

而缺少了window.external,就更甭提了,因此,这三者缺一不可。

 

再看看这个window.external,在常见的javascript书中不见踪影,但却非常有用,一个常见的应用是:

<input type="button" name="Button" value="add" οnclick="window.external.AddFavorite(location.href,document.title)" />

 

引用别人的"在嵌入了浏览器的工程中,除了IE默认提供的外部方法之外,需要网页的脚本中能调用c++代码,要实现这种交互,就必须实现脚本扩展。实现脚本扩展就是在程序中实现一个IDispatch接口,通过CHtmlView类的OnGetExternal虚函数返回此接口指针,这样就可以在脚本中通过window.external.XXX(关键字window可以省略)来引用接口暴露的方法或属性(XXX为方法或属性名)。"

 

再看看在c#中的脚本扩展,只需要webBrowser1.ObjectForScripting和ComVisibleAttribute(true)简单一设置就完事了,简单吧!幸福吧!悲催吧!

三.再来看一个,从winform的code里,能调用html页面里的javascript吗?
Page1.htm,删掉button,只保留javascript脚本。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
 <head>
  <title></title>
        <script type="text/javascript">
            function test(message) 
            {
                alert(message); 
            }
        </script>
 </head>
 <body>
    
 </body>
</html>

using System;
using System.Windows.Forms;
using System.Security.Permissions;
  
namespace WindowsFormsApp
{
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    [System.Runtime.InteropServices.ComVisibleAttribute(true)]
    public partial class Form2 : Form
    {
        private WebBrowser webBrowser1 = new WebBrowser();
        private Button button1 = new Button();

        public Form2()

        {
            InitializeComponent();

            button1.Text = "call script code from client code";

            button1.Dock = DockStyle.Top;
            button1.Click += new EventHandler(button1_Click);
            webBrowser1.Dock = DockStyle.Fill;
            Controls.Add(webBrowser1);
            Controls.Add(button1);
            Load += new EventHandler(Form2_Load);

        }

        private void Form2_Load(object sender, EventArgs e)

        {
            webBrowser1.AllowWebBrowserDrop = false;
            webBrowser1.IsWebBrowserContextMenuEnabled = false;
            webBrowser1.WebBrowserShortcutsEnabled = false;
            webBrowser1.ObjectForScripting = this;
            webBrowser1.Url= new Uri("C:\\workspace\\WindowsFormsApp\\WindowsFormsApp\\Page1.htm");
        }

        private void button1_Click(object sender, EventArgs e)

        {
            webBrowser1.Document.InvokeScript("test",
                new String[] { "called from client code" });
        }

    }

}

这回关键的因素就是webBrowser1.Document.InvokeScript了,而webBrowser1.ObjectForScripting,ComVisible不再需要了。

HtmlDocument.InvokeScript 方法的作用是:执行在 HTML 页面中定义的动态脚本函数。

至此,javascript与winform的code就可以互相调用了,感觉和web开发也有些类似。

 

这项技术叫在javascript(DHTML)代码和客户端应用程序代码之间实现双向通信.

--------------------------------------------------------------

1)在c#中调用js函数 

如果要传值,则可以定义object[]数组。 
具体方法如下例子: 
首先在js中定义被c#调用的方法: 
function Messageaa(message) 
      alert(message); 
在c#调用js方法Messageaa 
        private void button1_Click(object sender, EventArgs e) 
        { 
            // 调用JavaScript的messageBox方法,并传入参数 
            object[] objects = new object[1]; 
            objects[0] = "c# call javascript"; 
            webBrowser1.Document.InvokeScript("Messageaa", objects); 
        } 
2) 在js中调用c#方法 
在js中调用c#方法则相对比较简单: 
namespace WindowsFormsApplication1
{
     //申名托管类型,对com是可见的
     [System.Runtime.InteropServices.ComVisible(true)]
}
 private void Form1_Load(object sender, EventArgs e)
 {

     修改webbrowser的属性使c#可以调用js方法: 
     webBrowser1.ObjectForScripting = this; 
}
首先在c#中定义被js调用的方法: 
        public void MyMessageBox(string message) 
        { 
          MessageBox.Show(message); 
        } 
在js中调用c#方法: 
       <!-- 调用C#方法 --> 
       <button οnclick="window.external.MyMessageBox('javascript访问C#代码')" > 
       javascript访问C#代码</button>
你可能感兴趣的文章
请你不要侮辱我的劳动成果侮辱我的程序代码,我不是传说中的菜鸟,请你不要对我进行人身***。...
查看>>
一个老程序员未来10年的计划目标
查看>>
学会拒绝不是什么坏事,要鼓起勇气说“不”
查看>>
假导出Excel功能实现,按CSV格式快速导出功能代码参考(追求代码追求简洁复用)浙江杭州...
查看>>
我的友情链接
查看>>
keepalived主备切换后虚拟IP漂移慢的解决方法
查看>>
SAP系统参数设置
查看>>
LAMP架构之:httpd.conf配置文件解析
查看>>
oracle 锁
查看>>
[HDU]1728逃离迷宫
查看>>
中庸之道--适度的架构设计
查看>>
源代码实现LAMP的平台搭建
查看>>
echarts-简单入门
查看>>
thinkphp-循环输出标签volist
查看>>
bootstrap-无样式列表
查看>>
稳定性问题案例-Error -27796: Failed to connect to server
查看>>
L-1-17 Linux命令之压缩与归档命令
查看>>
Eclipse_java_log4j学习记录
查看>>
lvs haproxy lvs三种软负载均衡的比较
查看>>
Elasticsearch 索引、更新、删除文档
查看>>