zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    jsp转向
    什么是 XDoclet?
    tomcat中的几点配置说明
    mysql5问题
    POJ 3734 Blocks
    POJ 2409 Let it Bead
    HDU 1171 Big Event in HDU
    POJ 3046 Ant Counting
    HDU 2082 找单词
    POJ 1286 Necklace of Beads
  • 原文地址:https://www.cnblogs.com/linxuanchen/p/NanUI-Examples-04-Communicate-Between-CSharp-And-JS.html
Copyright © 2011-2022 走看看