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
using Coherent.UI.Binding;
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');
});
$('#QuitButton').click(function () {
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:
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()
{
view.RegisterForEvent("start", (System.Action)Game.Start);
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]
class Player
{
[CoherentProperty]
public string name;
[CoherentProperty("score")]
public double Score { get; set; }
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
{
class Player
{
}
}
$('#doCreatePlayer').click(function () {
var player = {
__Type: 'MyGame.Player, MyGame, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',
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)
{
return true;
}
else
{
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
{
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);
},
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;
});
function SaveGame(name)
{
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;
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;
});
});
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) { }
[CoherentProperty("LoadQuickSave")]
public bool Load() { }
public bool Load(int slot) { }
}