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) { }
}