Coherent UI for .NET  2.5.3
A modern user interface library for games
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events Pages
.Net Binding Sample

This is a C# port of the C++ Binding Sample and is built on top of the SlimDX Sample.

For instructions on deploying the sample, please read .NET Sample guides.

For a more detailed explanation of how binding works, you can check the Detailed Binding Guide

The .Net Binding sample demonstrates communication between the .Net application code and the JavaScript universe of the view.

The key points of the sample are

Handling Coherent.UI.View.BindingsReady event

public MyViewListener(SlimDXApp app)
{
// ...
// attach to the ReadyForBindings event to register the C# handlers
this.ReadyForBindings += this.RegisterBindings;
}

Registering delegates to handle events and calls triggered from JavaScript Binding

public void RegisterBindings(int frame, string url, bool isMainFrame)
{
if (isMainFrame)
{
// bind Game.Options.ApplyOptions to "ApplyOptions" in JavaScript
m_View.BindCall("ApplyOptions", (Action<Options>)Game.Options.ApplyOptions);
m_View.BindCall("GetLatency", (Func<int>)Game.GetNetworkLatency);
m_View.BindCall("GetGameTime", (Func<int>)Game.GetGameTime);
m_View.BindCall("GetMath", (Func<BoundObject>)(
// bind the methods of MyMath, not the instance data
// MyMath will live as long as it is used by JavaScript
() => BoundObject.BindMethods(new MyMath())
));
// triggered by the view when it has loaded
m_View.RegisterForEvent("ViewReady", (Action)this.ViewReady);
}
}

The HTML and Javascript code for this sample is in Coherent/Samples/UI/bin/html/binding.html.

When the document has been loaded and C# handlers have been registered the Ready event is fired.

engine.on('Ready', function () {
// trigger event handled by C++
engine.trigger('ViewReady');
//get the exposed MyMath object
engine.call('GetMath').then(function (math) {
// store reference to the object
window.MyMath = math;
// register commands for the console
engine.trigger('gameConsole:AddCommand', 'sum', 'computes the sum of its arguments', function (line) {
var numbers = line.split(/\s+/).map(Number);
numbers.splice(0, 1);
window.MyMath.Sum(numbers).then(function (sum) {
engine.trigger('gameConsole:Trace', 'The sum of', numbers, 'is', sum);
});
});
engine.trigger('gameConsole:AddCommand', 'avg', 'computes the average of its arguments', function (line) {
var numbers = line.split(/\s+/).map(Number);
numbers.splice(0, 1);
window.MyMath.Average(numbers).then(function (avg) {
engine.trigger('gameConsole:Trace', 'The average of', numbers, 'is', avg);
});
});
});
});

This actually triggers

private void ViewReady()
{
Game.Options.SetView(m_View);
// show the options
m_View.TriggerEvent("OpenOptions", Game.Options.Options);
}

Options is a C# type and must be exposed to JavaScript.

// all properties / fields for Options will be visible to Coherent UI
[CoherentType(PropertyBindingFlags.All)]
struct Options
{
public string Backend;
public uint Width;
public uint Height;
public string Username
{
get {
return System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString();
}
}
// rename the NetPort property to NetworkPort
[CoherentProperty("NetworkPort")]
public uint NetPort { get; set; }
}
Note
By default all public fields and properties of a C# type are visible to CoherentUI with the same names they have in *.Net*.

JavaScript functions bound to event "OptionOptions" receive an JavaScript object with the described properties and values taken from the C++ options object given to Coherent::UI::View::TriggerEvent.

function openOptions(options) {
$("#options").dialog( "open" );
$("#backend").text(options.Backend);
$("#gameWidth").val(options.Width);
$("#gameHeight").val(options.Height);
$("#user").text(options.Username);
$("#netPort").val(options.NetworkPort);
}
engine.on("OpenOptions", openOptions);

If JavaScript calls .Net function that takes an Options as an argument, the JavaScript object must have a property __Type with the AssemblyQualifiedName or at least the FullName of the type. For more details see User Defined Types .

function onApplyButton(){
var options = {};
options.__Type = gameOptionsTypeName;
options.Backend = $("#backend").text();
options.Width = Number($("#gameWidth").val());
options.Height = Number($("#gameHeight").val());
options.Username = $("#user").text();
options.NetworkPort = Number($("#netPort").val());
// This will call the C++ engine code with the just created structure. It'll be correclty populated
engine.call("ApplyOptions", options);
}

This sample also shows how to use binding of .Net methods. For detailed explanation see Exposing object methods .

The sample exposes an instance of MyMath to JavaScript.

class MyMath
{
public double Sum(double[] numbers)
{
return numbers.Sum();
}
public double Average(double[] numbers)
{
return numbers.Average();
}
}

When the game is up and running, we request the MyMath instance, store it as a global object and register two console commands that use it.

engine.on('Ready', function () {
//get the exposed MyMath object
engine.call('GetMath').then(function (math) {
// store reference to the object
window.MyMath = math;
// register commands for the console
engine.trigger('gameConsole:AddCommand', 'sum', 'computes the sum of its arguments', function (line) {
var numbers = line.split(/\s+/).map(Number);
numbers.splice(0, 1);
window.MyMath.Sum(numbers).then(function (sum) {
engine.trigger('gameConsole:Trace', 'The sum of', numbers, 'is', sum);
});
});
engine.trigger('gameConsole:AddCommand', 'avg', 'computes the average of its arguments', function (line) {
var numbers = line.split(/\s+/).map(Number);
numbers.splice(0, 1);
window.MyMath.Average(numbers).then(function (avg) {
engine.trigger('gameConsole:Trace', 'The average of', numbers, 'is', avg);
});
});
});
});

To try out the commands press ~ to open the game console and type sum 40 2.