NanUI文档目录

如何实现C#与Javascript的相互通信

通过之前的文章,相信您已经对NanUI有了初步的了解。但到目前为止,我们使用NanUI仅仅只是作为呈现HTML界面的容器,并未涉及CEF与C#间数据的交互。那么本文将简单介绍如何在NanUI中使用C#调用Javascript的函数以及如何在Javascript注入C#的对象、属性和方法。

C#调用Javascript函数

不需要获取返回值的情况

假设页面中有如下Javascript的函数sayHello,它的作用是在DOM中创建一个包含有“Hello NanUI!”字样的p元素。

function sayHello() {
var p = document.createElement("p");
p.innerText = "Hello NanUI!"; var container = document.getElementById("hello-container");
container.appendChild(p);
}

示例中,该函数并没有在Javascript环境里调用,而是在页面加载完成后使用NanUI的ExecuteJavascript方法来调用它。ExecuteJavascript方法执行的返回结果为一个bool类型,它指示了这次有没有成功执行。

在窗体的构造函数中,通过注册Formium的LoadHandler中的OnLoadEnd事件来监测页面加载完成的情况,并在页面加载成功后调用JS环境中的函数sayHello。

namespace CommunicateBetweenJsAndCSharp
{
using NetDimension.NanUI;
public partial class Form1 : Formium
{
public Form1()
: base("http://res.app.local/www/index.html",false)
{
InitializeComponent(); LoadHandler.OnLoadEnd += LoadHandler_OnLoadEnd;
} private void LoadHandler_OnLoadEnd(object sender, Chromium.Event.CfxOnLoadEndEventArgs e)
{
// Check if it is the main frame when page has loaded.
if(e.Frame.IsMain)
{
ExecuteJavascript("sayHello()");
}
}
}
}

运行后,可以看到界面中显示了“Hello NanUI!”字样,说明使用ExecuteJavascript能够调用JS函数。


需要获取返回值的情况

上面的例子中通过ExecuteJavascript方法来成功调用了JS环境中的函数。但不难发现,这种调用方式C#是没有接收到任何返回值的。但实际的项目里,我们是需要从JS环境获取到返回值的,这时候使用ExecuteJavascript将不能满足需求,使用另外一个方法EvaluateJavascript可以帮助我们从JS环境中获得JS函数的返回值。

假如有另外一个Javascript函数sayHelloToSomeone,它能接收一个字符传参数,在函数体中拼接并返回拼接后的字符串。

function sayHelloToSomeone(who) {
return "Hello " + who + "!";
}

同样的,在上面例子LoadHandler的OnLoadEnd事件中我们来执行sayHelloToSomeone,并通过C#传递参数并获取拼接后的返回值。EvaluateJavascript方法通过一个回调Action来获取JS环境中的返回值。这个Action有两个参数,第一个是返回值的集合,第二个是JS环境的异常对象,如果函数正确执行,那么第二个参数为null

namespace CommunicateBetweenJsAndCSharp
{
using NetDimension.NanUI;
public partial class Form1 : Formium
{
public Form1()
: base("http://res.app.local/www/index.html",false)
{
InitializeComponent(); LoadHandler.OnLoadEnd += LoadHandler_OnLoadEnd;
} private void LoadHandler_OnLoadEnd(object sender, Chromium.Event.CfxOnLoadEndEventArgs e)
{
// Check if it is the main frame when page has loaded.
if(e.Frame.IsMain)
{
EvaluateJavascript("sayHelloToSomeone('C#')", (value, exception) =>
{
if(value.IsString)
{
// Get value from Javascript.
var jsValue = value.StringValue; MessageBox.Show(jsValue);
}
});
}
}
}
}

在上面的示例中,通过我们可以明确知道JS函数sayHelloToSomeone的返回值一定为String类型,因此在C#的回调中直接使用Value的StringValue属性来获取JS函数的字符传返回值。但在实际的应用中,有可能并不完全知道返回值的类型,因此需要使用Value中内置的各个判断属性来逐一筛选返回值。

需要注意的是,Value的类是是ChromiumFX中的CfrV8Value类型,它是一个非常重要的类型,基本上所有在C#与CEF间的通信都是由这个类型来完成的。


Javascript调用C#对象及方法

简单的应用示例

上面的文章中演示了如何用C#来调用Javascript中的函数,那么下面的内容将介绍如何使用Javascript来调用C#中的对象、属性和各种方法。

在此之前,需要介绍NanUI窗体基类Formium中的重要属性GlobalObject,您可以把他理解成Javascript环境中的window对象。如果您需要在Javascript环境下使用C#中的各种对象、属性和方法,都需要将这些对象、属性、方法注册到GlobalObject里。

下面的例子,通过在Form1的构造函数中注册一个名为my的JS对象,并在my内置一个只读属性name,以及showCSharpMessageBoxgetArrayFromCSharpgetObjectFromCSharp三个函数。

//register the "my" object
var myObject = GlobalObject.AddObject("my"); //add property "name" to my, you should implemnt the getter/setter of name property by using PropertyGet/PropertySet events.
var nameProp = myObject.AddDynamicProperty("name");
nameProp.PropertyGet += (prop, args) =>
{
// getter - if js code "my.name" executes, it'll get the string "NanUI".
args.Retval = CfrV8Value.CreateString("NanUI");
args.SetReturnValue(true);
};
nameProp.PropertySet += (prop, args) =>
{
// setter's value from js context, here we do nothing, so it will store or igrone by your mind.
var value = args.Value;
args.SetReturnValue(true);
}; //add a function showCSharpMessageBox
var showMessageBoxFunc = myObject.AddFunction("showCSharpMessageBox");
showMessageBoxFunc.Execute += (func, args) =>
{
//it will be raised by js code "my.showCSharpMessageBox(`some text`)" executed.
//get the first string argument in Arguments, it pass by js function.
var stringArgument = args.Arguments.FirstOrDefault(p => p.IsString); if (stringArgument != null)
{
MessageBox.Show(this, stringArgument.StringValue, "C# Messagebox", MessageBoxButtons.OK, MessageBoxIcon.Information); }
}; //add a function getArrayFromCSharp, this function has an argument, it will combind C# string array with js array and return to js context.
var friends = new string[] { "Mr.JSON", "Mr.Lee", "Mr.BONG" }; var getArrayFromCSFunc = myObject.AddFunction("getArrayFromCSharp"); getArrayFromCSFunc.Execute += (func, args) =>
{
var jsArray = args.Arguments.FirstOrDefault(p => p.IsArray); if (jsArray == null)
{
jsArray = CfrV8Value.CreateArray(friends.Length);
for (int i = 0; i < friends.Length; i++)
{
jsArray.SetValue(i, CfrV8Value.CreateString(friends[i]));
}
}
else
{
var newArray = CfrV8Value.CreateArray(jsArray.ArrayLength + friends.Length); for (int i = 0; i < jsArray.ArrayLength; i++)
{
newArray.SetValue(i, jsArray.GetValue(i));
} var jsArrayLength = jsArray.ArrayLength; for (int i = 0; i < friends.Length; i++)
{
newArray.SetValue(i + jsArrayLength, CfrV8Value.CreateString(friends[i]));
} jsArray = newArray;
} //return the array to js context args.SetReturnValue(jsArray); //in js context, use code "my.getArrayFromCSharp()" will get an array like ["Mr.JSON", "Mr.Lee", "Mr.BONG"]
}; //add a function getObjectFromCSharp, this function has no arguments, but it will return a Object to js context.
var getObjectFormCSFunc = myObject.AddFunction("getObjectFromCSharp");
getObjectFormCSFunc.Execute += (func, args) =>
{
//create the CfrV8Value object and the accssor of this Object.
var jsObjectAccessor = new CfrV8Accessor();
var jsObject = CfrV8Value.CreateObject(jsObjectAccessor); //create a CfrV8Value array
var jsArray = CfrV8Value.CreateArray(friends.Length); for (int i = 0; i < friends.Length; i++)
{
jsArray.SetValue(i, CfrV8Value.CreateString(friends[i]));
} jsObject.SetValue("libName", CfrV8Value.CreateString("NanUI"), CfxV8PropertyAttribute.ReadOnly);
jsObject.SetValue("friends", jsArray, CfxV8PropertyAttribute.DontDelete); args.SetReturnValue(jsObject); //in js context, use code "my.getObjectFromCSharp()" will get an object like { friends:["Mr.JSON", "Mr.Lee", "Mr.BONG"], libName:"NanUI" }
};

运行项目打开CEF的DevTools窗口,在Console中输入my,就能看到my对象的详细信息。

执行my.showCSharpMessageBox("SOME TEXT FROM JS")命令,将调用C#的MessageBox来现实JS函数中提供的“SOME TEXT FROM JS”字样。

执行my.getArrayFromCSharp()能够从C#中取到我们内置的字符串数组中的三个字符串。如果在函数中指定了一个数组作为参数,那么指定的这个数组将和C#的字符串数组合并。

> my.getArrayFromCSharp()
["Mr.JSON", "Mr.Lee", "Mr.BONG"] > my.getArrayFromCSharp(["Js_Bison", "Js_Dick"])
["Js_Bison", "Js_Dick", "Mr.JSON", "Mr.Lee", "Mr.BONG"]

执行my.getObjectFromCSharp()能够从C#返回我们拼装的对象,该对象有一个字符型的libName属性,以及一个字符串数组friends

> my.getObjectFromCSharp()
Object {libName: "NanUI", friends: Array(3)}

回调函数

回调函数是Javascript里面重要和常用的功能,如果您在JS环境中注册的方法具有函数型的参数(即回调函数),通过Execute事件的Arguments可以获得回调的function,并使用CfrV8Value的ExecuteFunction来执行回调。

//add a function with callback function

var callbackTestFunc = GlobalObject.AddFunction("callbackTest");
callbackTestFunc.Execute += (func,args)=> {
var callback = args.Arguments.FirstOrDefault(p => p.IsFunction);
if(callback != null)
{
var callbackArgs = CfrV8Value.CreateObject(new CfrV8Accessor());
callbackArgs.SetValue("success", CfrV8Value.CreateBool(true), CfxV8PropertyAttribute.ReadOnly);
callbackArgs.SetValue("text", CfrV8Value.CreateString("Message from C#"), CfxV8PropertyAttribute.ReadOnly); callback.ExecuteFunction(null, new CfrV8Value[] { callbackArgs });
}
};

在Console中执行callbackTest(function(result){ console.log(result); })将执行匿名回调,并获取到C#回传的result对象。

> callbackTest(function(result){ console.log(result); })
Object {success: true, text: "Message from C#"}

在大多数情况下,在Javascript中回调都是因为执行了一些异步的操作,那么如果这些异步的操作是在C#执行也是可行的,只是实现起来就比较复杂。下面将演示如何实现一个异步回调。

//add a function with async callback
var asyncCallbackTestFunc = GlobalObject.AddFunction("asyncCallbackTest");
asyncCallbackTestFunc.Execute += async (func, args) => {
//save current context
var v8Context = CfrV8Context.GetCurrentContext();
var callback = args.Arguments.FirstOrDefault(p => p.IsFunction); //simulate async methods.
await Task.Delay(5000); if (callback != null)
{
//get render process context
var rc = callback.CreateRemoteCallContext(); //enter render process
rc.Enter(); //create render task
var task = new CfrTask();
task.Execute += (_, taskArgs) =>
{
//enter saved context
v8Context.Enter(); //create callback argument
var callbackArgs = CfrV8Value.CreateObject(new CfrV8Accessor());
callbackArgs.SetValue("success", CfrV8Value.CreateBool(true), CfxV8PropertyAttribute.ReadOnly);
callbackArgs.SetValue("text", CfrV8Value.CreateString("Message from C#"), CfxV8PropertyAttribute.ReadOnly); //execute callback
callback.ExecuteFunction(null, new CfrV8Value[] { callbackArgs }); v8Context.Exit(); //lock task from gc
lock (task)
{
Monitor.PulseAll(task);
}
}; lock (task)
{
//post task to render process
v8Context.TaskRunner.PostTask(task);
} rc.Exit(); GC.KeepAlive(task);
}

在Console中执行asyncCallbackTest(function(result){ console.log(result); })将执行匿名回调,大约5秒后获取到C#回传的result对象。

> asyncCallbackTest(function(result){ console.log(result); })
Object {success: true, text: "Message from C#"}

以上,您已经简单了解了使用NanUI如何做到C#和Javascript的相互通信。NanUI基于开源项目ChromiumFX开发,因此C#与Javascript的交互与ChomiumFX保持一致,如果需要开发更加复杂的功能,请自行搜索和参考ChromiumFX的相关API及示例。

示例源码

git clone https://github.com/NetDimension/NanUI-Examples-04-Communicate-Between-CSharp-And-JS.git

社群和帮助

GitHub

https://github.com/NetDimension/NanUI/

交流群QQ群

521854872

赞助作者

如果你喜欢我的工作,并且希望NanUI持续的发展,请对NanUI项目进行捐助以此来鼓励和支持我继续NanUI的开发工作。你可以使用微信或者支付宝来扫描下面的二维码进行捐助。

NanUI文档 - 如何实现C#与Javascript的相互通信的更多相关文章

  1. NanUI文档 - 开始使用NanUI

    NanUI文档目录 NanUI简介 开始使用NanUI 打包并使用内嵌式的HTML/CSS/JS资源 使用网页来设计整个窗口 如何实现C#与Javascript相互掉用(待更新...) 如何处理Nan ...

  2. NanUI文档 - 打包并使用内嵌式的HTML/CSS/JS资源

    NanUI文档目录 NanUI简介 开始使用NanUI 打包并使用内嵌式的HTML/CSS/JS资源 使用网页来设计整个窗口 如何实现C#与Javascript相互掉用(待更新...) 如何处理Nan ...

  3. NanUI文档 - 使用网页来设计整个窗口

    NanUI文档目录 NanUI简介 开始使用NanUI 打包并使用内嵌式的HTML/CSS/JS资源 使用网页来设计整个窗口 如何实现C#与Javascript相互掉用(待更新...) 如何处理Nan ...

  4. Prism 4 文档 ---第9章 松耦合组件之间通信

    当构建一个大而负责的应用程序时,通用的做法时将功能拆分到离散的模块程序集中.将模块之间的静态引用最小化.这使得模块可以被独立的开发,测试,部署和升级,以及它迫使松散耦合的沟通. 当在模块之间通信时,你 ...

  5. android和javascript之间相互通信实例分析

    1.  AndroidManifest.xml中必须使用许可 "android.permission.INTERNET", 否则会出Web page not available错误 ...

  6. JavaScript 客户端JavaScript之 脚本化文档

    客户端JavaScript的存在把静态HTML转变为交互式的Web应用程序,脚本化Web页面的内容正是JavaScript存在的理由.   一个文档对象模型或者说DOM就是一个API,它定义了如何访问 ...

  7. JavaScript的DOM(文档对象)基础语法总结2

    1.getAttribute()方法,通过元素节点的属性名称获取属性的值. //语法 elementNode.getAttribute(name) //element(元素);Node(节点) //注 ...

  8. 学习笔记:javascript 文档对象(document)

    1.documnet函数 方法 描述 close() 关闭用 document.open() 方法打开的输出流,并显示选定的数据. getElementById() 返回对拥有指定 id 的第一个对象 ...

  9. javascript文档

    DOM Document <html> Document 对象 每个载入浏览器的 HTML 文档都会成为 Document 对象. Document 对象使我们可以从脚本中对 HTML 页 ...

随机推荐

  1. 黑马day16 jquery&amp;属性过滤选择器

    属性过滤选择器的过滤规则是通过元素的属性来获取对应的元素 .[attribute] 使用方法: $("div[id]") ;  返回值  集合元素 说明:匹配包括给定属性的元素.样 ...

  2. HDU 4911 Inversion 树状数组求逆序数对

    显然每次交换都能降低1 所以求出逆序数对数,然后-=k就好了.. . _(:зゝ∠)_ #include<stdio.h> #include<string.h> #includ ...

  3. 《深入理解java虚拟机》笔记——简析java类文件结构

    一直不太搞得明确jvm究竟是如何进行类载入的,在看资料的过程中迷迷糊糊.在理解类载入之前,首先看看java的类文件结构究竟是如何的,都包含了哪些内容. 最直接的參考当然是官方文档:The Java® ...

  4. 一张图让你明确Android Touch事件的传递机制

  5. IT(计算机/软件/互联网)专业词汇宝典(持续更新中)

    1.Stack Overflow:http://stackoverflow.com/      .一个著名的IT技术的问答站点.全然免费.程序猿必知. 2.programmer:程序猿 3.enthu ...

  6. JQuery插件开发标准写法

    ;//step01 定义JQuery的作用域 (function ($) { //step03-a 插件的默认值属性 var defaults = { prevId: 'prevBtn', prevT ...

  7. 一 、Spring Boot 学习之项目搭建

    一.简介 spring 官方网站本身使用Spring 框架开发,随着功能以及业务逻辑的日益复杂,应用伴随着大量的XML配置文件以及复杂的Bean依赖关系. 随着Spring 3.0的发布,Spring ...

  8. loadrunner设置分压机 压力测试

    压力机设置分压机使用场景: 当本地主机内存较小,运行速度较慢,压力较大等多种需求场景下,可以使用分压机. 一.将另一台机器设置为压力机 1,开始菜单找到HP LoadRunner-->Advan ...

  9. HTML基础教程-标题

    HTML 标题 在 HTML 文档中,标题很重要. HTML 标题 标题(Heading)是通过 <h1> - <h6> 等标签进行定义的. <h1> 定义最大的标 ...

  10. Java I/O---输入与输出

    编程语言的I/O类库中常使用流这个抽象概念, 它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象. "流" 屏蔽了实际的I/O设备中处理数据的细节.Java类库中 ...