piątek, 24 października 2008

Komunikacja pomiędzy Silverlight-em 2.0, a kodem javascript

W poprzednim artykule poświeconym połączeniom pomiędzy Silverlight-em, a kodem HTML (link), pokazałem jak w prosty sposób wskazać elementy strony html z poziomu aplikacji Silverlight i je użyć. Dowiedzieliśmy się także, w jaki sposób podpiąć Silverlight-a pod zdarzenia z kodu html. Całość zaowocowała nam "grą" w Ping Ponga :)

W tym artykule, chciałem krótko pokazać w jaki sposób możemy wywołać metody aplikacji Silverlight-a z kodu javascript i na odwrót, czyli metody javascript z kodu aplikacji Silverlight. Funkcjonalność ta okazuje się bardzo przydatna, szczególnie przy pracy z Virtual Earthem, Google Maps, obiektami JSONowymi i innymi aplikacjami/skryptami stworzonymi w javascript-cie.

Agenda

  1. Odwołanie do metody w javascript-cie z Silverlight-a
  2. Odwołanie do kodu aplikacji Silverlight, z javascript-u
  3. Obsługa event-ów wywołanych w Silverlight-cie, w kodzie javascript
  4. Przykładowy działający kod

Odwołanie do metody w javascript-cie z Silverlight-a

W pliku cs (np. Page.cs) dodajemy import Browser-a:

using System.Windows.Browser;

a w pliku javascript (np. Test.js), utworzonym przez nas w projekcie, dodajemy metodę

function testMethod(nothing) {
window.alert("[javascript] hello from javascript!");
}

i wywołujemy ją z kodu Silverlight-a (Page.cs):

Object[] args = null;
Object result = HtmlPage.Window.Invoke("testMethod", args);

, parametr args jest opcjonalny, tak samo jak wartość zwracana - result.

Plik Test.js musi być podłączony do strony html/aspx, która zawiera aplikacje Silverlight

<script type="text/javascript" src="Test.js"></script>

Alternatywnie, metoda testMethod może znajdować się bezpośrednio w pliku html/aspx, za pośrednictwem znaczników

<script type="text/javascript"> ... </script>

Odwołanie do kodu aplikacji Silverlight, z javascript-u

W celu odwołania się do Silverlight-a, musimy oznaczyć nasze zmienne, klasy, properties-y i eventy specjalnymi znacznikami ([ScriptableType], [ScriptableMember]). Dlatego też, klasa do której będziemy mogli się odwołać z javascript-u, będzie miała ten atrybut

[ScriptableType]
public class ScriptableClass
{
}

, natomiast jej zmienne i metody muszą mieć znaczniki ScriptableMember, np.:

[ScriptableType]
public class ScriptableClass
{
[ScriptableMember]
public string ScriptableMethod(string value){…}

[ScriptableMember]
public string val
{
get { return _val; }
set { _val = value; }
}
}

Następnie klasa musi zostać zarejestrowana na stronie html, poprzez wywołanie (np. w pliku Page.cs):

ScriptableClass scriptableClass;
scriptableClass = new ScriptableClass();
HtmlPage.RegisterScriptableObject("ScriptableClass", scriptableClass);

Na końcu, aby odwołać się do obiektu scriptableClass i jego metod/zmiennych w kodzie javascript, piszemy (w pliku Test.js):

var control = document.getElementById("Xaml1");
result = control.Content.ScriptableClass.scriptableMethod("Hello from javascript");

, a więc odnajdujemy kontrolkę Silverlight-a po jej id na stronie html/aspx (tu ta wartość może się u Was różnić jeśli modyfikowaliście kod html/aspx, w moim wypadku jest to "Xaml1") i wskazujcie zarejestrowany obiekt (ScriptableClass) i wywołujecie na nim metody (np. scriptableMethod(..)).

<div style="height: 100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightApplication1.xap"
MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />
</div>

Mamy już wszystko co trzeba. Po wywołaniu metody scriptableMethod() z klasy ScriptableClass w kodzie javascript, wykona się metoda w kodzie Silverlight-a.

Obsługa event-ów wywołanych w Silverlight-cie w kodzie javascript

Aby event mógł być obsłużony, należy utworzyć EventHandler (np. public event EventHandler SilverHandler) w aplikacji (ScriptableClass.cs)

[ScriptableType]
public class ScriptableClass
{
[ScriptableMember]
public event EventHandler SilverEvent;

public virtual void OnEvent(ScriptableEventArgs e)
{
if (null != SilverEvent)
{
SilverEvent(this, e);
}
e.val.ToString(); //value from javascript
}
}

Dodaliśmy także metodę OnEvent do ScriptableClass, która wywoła event SilverEvent. Teraz potrzebujemy metody która wywoła OnEvent (Page.cs):

ScriptableClass scriptableClass;

private void Button_Click_1(object sender, RoutedEventArgs e)
{
ScriptableEventArgs sea = new ScriptableEventArgs();
sea.val = "Event Args from Silverlight";

scriptableClass.OnEvent(sea);
}

Jak widać, pojawiła się dodatkowo klasa ScriptableEventArgs. Klasa ta, to nadpisana własna klasa EventArgs-ów. Musimy ją nadpisać, ponieważ potrzebujemy EventArgs-ów które są także typu [ScriptableType] ( ScriptableEventArgs.cs.)

[ScriptableType]
public class ScriptableEventArgs : EventArgs
{
private string _val;

[ScriptableMember]
public string val
{
get { return _val; }
set { _val = value; }
}
}

A więc mamy event (SilverEvent), mamy klasę argumentów (ScriptableEventArgs) oraz metodę która wywoła event. To co potrzebujemy, to obsługi eventa po stronie javascript (Test.js)

function loadedd(cos) {
var control = document.getElementById("Xaml1");
control.Content.ScriptableClass.SilverEvent = eventfunction;
}

function eventfunction(sender, args) {
value = args.val;
window.alert("[javascript] " + value);
args.val = "javascript values"; //return string
}

I tak, metoda eventfunction zostanie wywołana w momencie wciśnięcia przycisku w aplikacji Silverlight (Button_Click_1), ponieważ została przypisana do eventa SilverEvent. Przypisanie robimy podobnie jak dla wywołania metod z obiektu ScriptableClass, tylko tym razem przypisujemy metodę pod event.

control.Content.ScriptableClass.SilverEvent = eventfunction;

Ponieważ przypisanie może zostać wykonane dopiero po inicjalizacji aplikacji Silverlight, dlatego też w/w przypisanie eventa zostało zamknięte w metodę, którą ja wywołuje dopiero z metody Page_Loaded

public Page()
{
InitializeComponent();

this.Loaded += new RoutedEventHandler(Page_Loaded);
}

void Page_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.Window.Invoke("loadedd");
}

W ten sposób mam pewność, że event SilverEvent zostanie prawidłowo przypisany.

Przykładowy działający kod

Dla celów tego artykułu, stworzyłem demo aplikacji, która zapewnia prostą komunikację pomiędzy Silverlightem i javascriptem. W aplikacji mamy trzy przyciski:

  • "Call javascript" – wywoła metodę w kodzie javascript (Test.js) z aplikacji Silverlight
  • "Call Silverlight" – wywoła metodę javascritp, która wywoła metodę w aplikacji Silverlight
  • "Call javascript event" – zostanie wywołany event (SilverEvent) w obiekcie ScriptableClass. Event zostanie przekazany i obsłużony w kodzie javascript. Kod javascript zwróci rezultaty poprzez użycie wartości w klasie ScriptableEventArgs

Plik Page.xaml:

<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Canvas x:Name="LayoutRoot" Background="White">
<Button Content="Call javascript" Click="Button_Click" Canvas.Left="100" Canvas.Top="50" />
<Button Content="Call javascript event" Click="Button_Click_1" Canvas.Left="100" Canvas.Top="100" />
</Canvas>
</UserControl>

Plik SilverlightApplicationTestPage.aspx

<%@ Page Language="C#" AutoEventWireup="true" %><%@ Register
Assembly="System.Web.Silverlight" Namespace="System.Web.UI.SilverlightControls"
TagPrefix="asp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="height: 100%;">
<head runat="server">
<title>SilverlightApplication1</title>

<script type="text/javascript" src="Test.js"></script>

</head>
<body style="height: 100%; margin: 0;">
<form name="CallButtons" action="" method="GET">

<input type="button" name="button" value="Call Silverlight" onclick="testScriptableClassMethod()">

</form>
<br />
<form id="form1" runat="server" style="height: 100%;">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div style="height: 100%;">

<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightApplication1.xap"
MinimumVersion="2.0.31005.0" Width="100%" Height="100%" />

</div>
</form>
</body>
</html>

Plik Page.cs

public partial class Page : UserControl
{
ScriptableClass scriptableClass;
public Page()
{
InitializeComponent();
scriptableClass = new ScriptableClass("Hello from Silverlight");
HtmlPage.RegisterScriptableObject("ScriptableClass", scriptableClass);

this.Loaded += new RoutedEventHandler(Page_Loaded);
}

void Page_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.Window.Invoke("loadedd");
}

private void Button_Click(object sender, RoutedEventArgs e)
{
Object[] args = null;
Object result = HtmlPage.Window.Invoke("testMethod", args);
}

private void Button_Click_1(object sender, RoutedEventArgs e)
{
ScriptableEventArgs sea = new ScriptableEventArgs();
sea.val = "Event Args from Silverlight";
scriptableClass.OnEvent(sea);
}
}

Plik ScriptableClass.cs

[ScriptableType]
public class ScriptableClass
{
private string value;

[ScriptableMember]
public event EventHandler SilverEvent;

public ScriptableClass(string val)
{
value = val;
}

[ScriptableMember]
public string scriptableMethod(string val)
{
string temp = value;
value = val;
return temp;
}

public virtual void OnEvent(ScriptableEventArgs e)
{
if (null != SilverEvent)
{
SilverEvent(this, e);
}
e.val.ToString(); //value from javascript
}
}

Plik ScriptableEventArgs.cs

[ScriptableType]
public class ScriptableEventArgs : EventArgs
{
private string _val;

[ScriptableMember]
public string val
{
get { return _val; }
set { _val = value; }
}
}

Plik Test.js

function testMethod(nothing) {
window.alert("[javascript] hello from javascript!");
}


function testScriptableClassMethod() {
var control = document.getElementById("Xaml1");
result = control.Content.ScriptableClass.scriptableMethod("Hello from javascript");

window.alert("[javascript] " + result);
}

function eventfunction(sender, args) {
value = args.val;
window.alert("[javascript] " + value);
args.val = "javascript values";
}

function loadedd(cos) {
var control = document.getElementById("Xaml1");

control.Content.ScriptableClass.SilverEvent = eventfunction;
}

Pełny kod dema znajdziecie tu -> link

Resources


Pozdrawiam,
JAcek Ciereszko

Brak komentarzy: