Ricordo che all'incirca nel 2003, quando si stava affacciando Windows Vista, WPF e le tecnologie che oggi sono una realtà nel .net Framework, posi ad un caro amico ed allora Evangelista in Microsoft la seguente domanda: "perchè non ci fate un "browser managed", che possa "dialogare" con dei controlli winform ospitati nel browser stesso?" Allora i controlli winform erano l'unica strada se si volevano sviluppare delle "applet" ospitate nel browser. Sarò sincero: quella strada non ha portato molto lontano, e con l'avvento di AJAX e di un framework consistente per la comunicazione asincrona sul web sono venuti meno i requisiti che potevano spingerci in quella direzione.
Ora però, dopo un pò di anni, mi sono ritrovato tra le mani la tecnologia che tanto avevo desiderato: non abbiamo un browser "managed", ma abbiamo Silverlight!
E' un pò come Maometto e la montagna... invece di fare un browser managed, Silverlight permette la comunicazione bidirezionale tra la l'applicazione e la pagina che la ospita. La comunicazione, però, supera la semplice interazione con qualche controllo input o di layout della pagina, e si spinge all'interazione con il codice Javascript contenuto nella pagina stessa. Vi dirò di più: anche il codice Javascript, sotto determinate condizioni, può interagire con i moduli, invocando metodi pubblici "esposti" dal modulo alla pagina.
Cosa si può fare con questa roba? In pratica si possono far comunicare tra loro diversi moduli ospitati nella stessa pagina e sviluppati separatamente. Vediamo di scendere nel codice per capire come fare...
Quello che andremo a sviluppare sono due applicazioni Silverlight che verranno ospitate in una banalissima applicazione web. Le due applicazioni si chiameranno SilverlightAppOne e SilverlightAppTwo. La prima ci servirà per illustrare come comunicare dall'applicazione alla pagina, mentre la seconda come comunicare dalla pagina all'applicazione. Infine cercheremo di combinare i due concetti per comunicare da un'applicazione all'altra, sfruttando la pagina come "zona franca".
Comunicazione parte prima: Silverlight=>HTML & Javascript
La prima cosa da investigare è la comunicazione tra una Silverlight application e la pagina che la ospita. Il namespace che ci può aiutare in questa attività è System.Windows.Browser. In questo namespace, la classe HtmlPage espone la proprietà Document, che ci consente di accedere ad ogni elemento della pagina. Utilizzando questi oggetti si possono ad esempio ottenere questi risultati:
1: //Ottiene un riferimento all'oggetto myElement contenuto nella pagina
2: HtmlElement _myElement = System.Windows.Browser.HtmlPage.Document.GetElementById("myElement");
3: //Creo un nuovo figlio per l'elemento myelement nel DOM
4: HtmlElement _anotherElement=System.Windows.Browser.HtmlPage.Document.CreateElement("DIV");
5: _myElement.AppendChild(_anotherElement);
6: //ottiene un riferimento alle coppie chiave/valore componenti la querystring della pagina
7: IDictionary<string,string> _qs = System.Windows.Browser.HtmlPage.Document.QueryString;
Oltre alla proprietà Document ce n'è un'altra che possiamo utilizzare per i nostri scopi: la proprietà Window. Questa permette di ottenere un riferimento alla finestra del browser e di richiamare il metodo Invoke. Questo metodo permette di invocare una funzione Javascript presente all'interno della pagina HTML, eventualmente passando dei parametri alla funzione stessa. La sintassi è molto semplice, e la possiamo vedere nel seguente snippet:
1: //HtmlPage.Window.Invoke(string name, params object[] args)
2: HtmlPage.Window.Invoke("MyFunction", "param1", "param2");
Quindi, nel nostro caso andremo a scrivere una funzione javascript molto semplice che possa essere chiamata dalla nostra Silverlight Application e che mostri un alert:
1: //Managed Event Handler (inside Silverlight application)
2: private void btnCommunicate_Click(object sender, RoutedEventArgs e)
3: {
4: HtmlPage.Window.Invoke("Dispatch", txtValue.Text);
5: }
6: //Javascript function (inside HTML Page)
7: function Dispatch(param) {
8: alert('Hi! Thi message comes from the SilverlightAppOne: ' + param);
9: }
Con questo abbiamo concluso la prima parte, ovvero richiamare del codice javascript da una applicazione Silverlight. Ora cerchiamo di capire come implementare la seconda parte, ovvero come invocare una funzione managed esposta da una Silverlight Application da codice javascript collocato all'inteno della pagina host.
Comunicazione parte seconda Javascript=>Silverlight
La comunicazione da codice Javascript verso un'applicazione Silverlight è un pò più "articolata", ma comunque molto semplice. In pratica, Il namespace System.Windows.Browser contiene una classe ScriptableMemberAttribute che permette di decorare funzioni e/o metodi in modo che siano "visibili" da codice Javascript. Questo però non basta per rendere invocabile il nostro metodo dal codice Javascript; sarà necessario "registrare" il nostro oggetto in modo da renderlo "visibile" al codice script della pagina che lo contiene. Questo si fa molto semplicemente richiamando il metodo RegisterScriptableObject(name, object) nell' event handler dello StartUp dell'applicazione Silverlight. L'oggetto può essere un qualunque nostro oggetto che esponga dei metodi "scriptable". Noi utilizzeremo la nostra Page dell'applicazione Silverlight, creando un metodo ExposedFunction:
1: //Application Startup Event Handler
2: private void Application_Startup(object sender, StartupEventArgs e)
3: {
4: Page _page = new Page();
5: HtmlPage.RegisterScriptableObject("AppTwo", _page);
6: this.RootVisual = _page;
7: }
8:
9: //Exposed Function inside Page Class
10: [ScriptableMember]
11: public void ExposedFunction(string param)
12: {
13: txtValue.Text = param;
14: }
15:
16: //Javascript Code to invoke function from page
17: document.getElementById('hostAppTwo').Content.AppTwo.ExposedFunction(param);
Ovviamente, nella pagina HTML che ospita i controlli Silverlight, si devono impostare correttamente gli id dei controlli stessi, ma questo lo lascio controllare a voi nel codice dell'applicazione di esempio a corredo di questo articolo. La cosa che conta è che da Javascript sia possibile invocare codice managed all'interno dell'applicazione Silverlight.
Communication Established! Silverlight => Javascript => Silverlight
Ora viene il bello! Con una modifica banalissima riusciremo a far arrivare il valore inserito nella Texbox di AppOne nella textBox di AppTwo... e questo non è male! La modifica in questione consiste nell'inserire la chiamata Content.AppTwo.ExposedFunction(param) all'interno della function Dispatch che abbiamo visto in precedenza.
Conclusioni
Questo sistema di comunicazione basato sull'invocazione di metodi esposti da applicazioni Silverlight si presta alla creazione di mashup applications che permettono di combinare tra loro più controlli Silverlight sviluppati anche in modo indipendente uno dall'altro. Sarà sufficiente documentare l'interfaccia di comunicazione di ogni controllo in modo che gli sviluppatori possano utilizzare javascript come "collante" tra i moduli. Se volete avere una panoramica più precisa, tutto il codice lo potete scaricare qui.