• NanUI文档


    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的开发工作。你可以使用微信或者支付宝来扫描下面的二维码进行捐助。

    Screen Shot

  • 相关阅读:
    [置顶] 搭建一个流媒体服务器引子
    Exchange Server 2007 常见问题解答(6)
    [置顶] 第九周项目1
    iOS 6应用开发实战
    hdu 1722(数论)
    js二维数组排序
    HDU 4027 线段树 Can you answer these queries?
    Socket编程指南及示例程序
    Spring攻略学习笔记(2.13)解析文本消息
    线性渐变lineargradient和滤镜opacity/filter的透明效果兼容性解决方案及其RGB/RGBA与16进制转换方法
  • 原文地址:https://www.cnblogs.com/linxuanchen/p/NanUI-Examples-04-Communicate-Between-CSharp-And-JS.html
Copyright © 2020-2023  润新知