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

Coherent UI allows easy communication between your application and the UI via several mechanisms.

Events

.Net triggering JavaScript

To trigger an event for the view use

// bring in TriggerEvent overloads with one and more arguments
using Coherent.UI.Binding;
// ...
// set player score to 10000
view.TriggerEvent('PlayerScoreChanged', 10000);

For the view to be able to catch this event it must have executed:

engine.on('PlayerScoreChanged', function (new_score) {
var scoreDisplay = document.getElementById('scoreDisplay');
scoreDisplay.innerHTML = new_score;
});
See Also
For more information about engine, see JavaScript.

JavaScript triggering .Net

.Net delegates for events triggered by JavaScript should be registered in the handler of the Coherent::UI::ViewListener::OnReadyForBindings event.

Event Handlers

Event handlers are registered with the Coherent.UI.View.RegisterForEvent method. They cannot return any value to JavaScript, but may trigger an event for JavaScript. There may be more than one .Net handler for a given event. There also may be both .Net and JavaScript (and even C++) handlers for the same event.

Warning
JavaScript handlers for an event are called synchronously, but .Net and JavaScript handlers for an event are called asynchronously. There is no guarantee for the order of execution of .Net and JavaScript event handlers.
There is no guarantee for the order of execution of .Net event handlers.
class Game
{
public void Quit()
{
}
public static Game Instance;
}
class GameViewListener : public Coherent.UI.ViewListener
{
public virtual void OnReadyForBindings(int frameId, string url, bool isMainFrame)
{
m_View.RegisterForEvent("OnQuitClicked", (System.Action)Game.Instance.Quit);
}
};

Triggering the event from JavaScript looks like:

engine.on('OnQuitClicked', function () {
ShowMessage('Bye');
});
// using jQuery to simplify the sample
$('#QuitButton').click(function () {
// this will execute both Game::Quit in .Net
// and ShowMessage('Bye') in JavaScript
engine.trigger('OnQuitClicked');
});

Call Handlers

Call handlers are registered with the Coherent.UI.View.BindCall method. There may be only one handler for a given call and the handler may return a result.

class Game
{
public string GetPlayerName()
{
return m_PlayerName;
}
public static Game Instance;
}
class GameViewListener : public Coherent.UI.ViewListener
{
public void OnReadyForBindings(int frameId, string url, bool isMainFrame)
{
m_View.BindCall("getPlayerName", (System.Func<string>)Game.Instance.GetPlayerName);
}
}

To get the player name in the view is:

// call 'Game.GetPlayerName' with callback for the result
engine.call('getPlayerName').then(function (name) {
var playerName = document.getElementById('playerName');
playerName.innerHTML = name;
});

Handlers with arguments

Coherent.UI.View.RegisterForEvent and Coherent.UI.View.BindCall take a System.Delegate as an argument. Coherent UI uses reflection to determine the signature of the actual delegate so that it can convert the arguments from JavaScript and invoke the delegate. All you have to do is give Coherent UI a delegate instance.

public void RegisterBindings()
{
// create a "void ()" delegate from Game.Start
view.RegisterForEvent("start", (System.Action)Game.Start);
// create a "Item (int)" delegate from thePlayer.GetItem
view.BindCall("GetItem", (System.Func<int, Item>)thePlayer.GetItem);
}

Context Events

There are two context events:

  • Ready - triggered when the page loading has finished and Coherent.UI.ViewListener::OnReadyForBindings as been executed. Triggering events for .Net before Ready will result in strange 'disappearance' of events.
  • Error - triggered when there was an error converting arguments from JavaScript to .Net (i.e. the .Net function expects a number, but JavaScript gives a string)

Unregistering Handlers

You can remove a particular event or call hander using Coherent.UI.View.UnregisterFromEvent and Coherent.UI.View.UnbindCall using the Coherent.UI.BoundEventHandle returned from Coherent.UI.View.RegisterForEvent and Coherent.UI.View.BindCall. You can also remove all handlers that are methods of a particular object at once using Coherent.UI.View.UnbindObject.

Warning
Unregistering or unbinding a handler inside the callback of the handler is not supported and will result in undefined behavior. If you want to remove a handler, schedule the removal to happen outside of the callback.

Exposing .Net Types

User Defined Types

Arbitrary .Net types (except long) as arguments or results for event and call handlers. By default, all public fields and properties are exposed to JavaScript. This behavior can be customized using Coherent.UI.Binding.CoherentType attribute.

using Coherent.UI.Binding;
[CoherentType] // by default explicitly expose properties
class Player
{
[CoherentProperty] // expose name
public string name;
[CoherentProperty("score")] // expose Score as "score"
public double Score { get; set; }
// Health remains invisible to JavaScript
public double Health;
}

When an instance of Player is used as an argument for a TriggerEvent, the exposed fields and properties are

class Game
{
public void Start()
{
m_View.TriggerEvent("StartWithPlayer", m_Player);
}
private Player m_Player;
}

In JavaScript, we receive an object with the specified properties. The value of each property is the same as in the moment of triggering the event. We may store a reference to this object, but its properties WILL NOT be synchronised with m_Player.

engine.on('StartWithPlayer', function (player) {
var playerName = document.getElementById('playerName');
playerName.innerHTML = player.name;
var scoreDisplay = document.getElementById('scoreDisplay');
scoreDisplay.innerHTML = player.score;
});
Warning
If you want to call .Net handler with an instance of Player created in JavaScript there is one important detail - the object must have a property __Type with value the assembly qualified name or at least the full name of the type. Otherwise Coherent UI cannot create an instance of Player.
namespace MyGame
{
// Assembly qualified name: MyGame.Player, MyGame, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// Full name: MyGame.Player
class Player
{
}
}
$('#doCreatePlayer').click(function () {
var player = {
__Type: 'MyGame.Player, MyGame, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',
// set the assembly qualified type name
// or set the full name of the type
// __Type: 'MyGame.Player',
name: $('#playerName').val(),
score: 0
};
engine.call('CreatePlayer', player).then(function (success) {
if (success) {
ShowMessage('Welcome ' + player.name);
} else {
ShowMessage('Sorry, try another name');
}
});
});
bool CreatePlayer(Player player)
{
if (player.name.IndexOf(' ') == -1)
{
// create the player
return true;
}
else
{
// sorry, no spaces in player name
return false;
}
}

For some calls it's possible that there is no meaningful value to return. For example -

Item GetItem(int slot)
{
if (HasItemAt(slot))
{
return GetItemAt(slot);
}
else
{
// notify the view that there is not item at this slot
m_View.SetScriptError(Coherent::UI::SCE_NoResult, "no item at slot");
return new Item();
}
}
engine.call('GetItem', slot).then(function (item) {
ShowMessage('Item at slot ' + slot + ' costs ' + item.Price);
},
// called when there is no item at this slot
function (errorType, message) {
console.log('could not get item at slot ' + slot);
}
});

Collections

Coherent UI has built-in support for arrays and types implementing System.Collections.Generic.IList or System.Collections.Generic.IDictionary interfaces, so your handlers can take as arguments or return to JavaScript any combination of these collections like Dictionary<string, int[]>.

Methods

Exposing object methods

To make interaction between .Net and JavaScript easier Coherent UI allows to expose an .Net object with its methods to JavaScript. This way the UI is able, not only to display the relevant information to the player, but actively to control and manipulate the game.

Note
Currently Coherent UI doesn't support invocation of overloaded methods. To resolve this rename the overloads using CoherentProperty. See Specifying exposed methods .

For example, lets have the following Game class

class Game {
public bool Save(string name)
{
// ..
}
public bool Load(string name)
{
// ..
}
}

You can expose a instance of Game to JavaScript by wrapping it using Coherent.UI.Binding.BoundObject.BindMethods and then sending it to JavaScript.

Exposing a Game instance as an argument of an event:

engine.on('startGame', function (game) {
GlobalGame = game; // store reference to the object
});
function SaveGame(name)
{
// all .Net methods of Game class are accessible with the same signature
// the .Net methods are executed asynchronously and return a promise
// for the result value in JavaScript
GlobalGame.Save(name).then(function (success) {
if (success) {
console.log("Game saved to ", name);
} else {
console.log("Could not save game to ", name);
}
},
}
using Coherent.UI.Binding;
// ..
Coherent.UI.View view = GetView();
Game gameInstance = GetGame();
view.TriggerEvent("startGame", BoundObject.BindMethods(gameInstance));

Exposing a Game instance as the result of a call handler:

engine.on('Ready', function () {
engine.call('getTheGame', function (game) {
GlobalGame = game; // store reference to the object
});
});
using Coherent.UI.Binding;
// ..
void OnReadyForBindings(int frameId, string url, bool isMainFrame)
{
if (isMainFrame)
{
View.BindCall("getTheGame", (Func<BoundObject>)this.GetGame);
}
}
BoundObject GetGame()
{
return BoundObject.BindMethods(m_Game);
}

Lifetime

Warning
To prevent usage of destroyed objects Coherent UI stores a reference to your exposed .Net instance while the representing object in JavaScript is still alive. To prevent "memory leaks" of .Net objects, be sure to remove all references to the JavaScript object.

Specifying exposed methods

Control over which .Net methods are exposed to JavaScript and their names is provided by CoherentType and CoherentProperty attributes. By default all public, instance methods are exposed. Here is sample how to expose only part of the methods of Game and rename one overload to allow exposing it to JavaScript:

using Coherent.UI.Binding;
[CoherentType]
class Game
{
[CoherentProperty]
public bool Save(string name) { }
[CoherentProperty]
public bool Load(string name) { }
// rename this Load overload to allow exposing to JavaScript
[CoherentProperty("LoadQuickSave")]
public bool Load() { } // Loads the "quick-save" slot
// do not expose this method to JavaScript
public bool Load(int slot) { }
}