Hay diferentes formas de incrustar una aplicación Silverlight en una Web, podemos ocupar toda la web con la aplicación, en cuyo caso no nos sería útil este manual. O podemos realizar varias aplicaciones colocadas en diferentes partes de nuestra web. Para los que necesiten que sus pequeñas aplicaciones Silverlight se comuniquen, están leyendo el manual idóneo.
Si pensamos un poco el funcionamiento de una aplicación Silverlight, al fin y al cabo tiene que poder llamar a funciones JavaScript, de lo contrario, ¿Cómo lanzaría un alert en una Web?¿Cómo sería capaz de responder a eventos?
Pues sí, debajo de la capa tan bonita que tenemos en Silverlight mediante la cual programamos todas las acciones producidas en la Web en código C# o VB hay unas bibliotecas que traducen ese C# o VB compilado a código entendible por la Web, por ejemplo, JavaScript.
Sin más explicaciones de arquitectura de Silverlight, cabe decir que Silverlight está preparado para ejecutar funciones JavaScript y para permitir que sus funciones sean invocadas desde código JavaScript.
Voy a ir poniendo pequeños ejemplos que irán formando una aplicación web, en la que habrá dos aplicaciones Silverlight comunicándose usando como puente las funciones JavaSript.
Los ejemplos los haré en C#, pero se dejarán los proyectos tanto para C# como para VB (a petición de mi amigo Dani Seara).
Requisitos (más info en http://silverlight.net/getstarted):
- Visual Studio 2008
- Silverlight Tools Beta 2 for Visual Studio 2008
- Expression Blend 2.5 June Preview
- Silverlight 2 beta 2
1. Invocar función JavaScript desde C# o VB
Supongamos este código JavaScript en una página HTML.
<script type="text/javascript">
function mostrarPantalla(cadena)
{
alert(cadena);
}
</script>
Y una aplicación Silverlight con un botón que al pulsarlo ejecuta el siguiente código:
HtmlPage.Window.Invoke("mostrarPantalla", txtEnviar.Text);
Solamente con este código ya podemos invocar la función mostarPantalla de JavaScript desde C#. El resultado es:

Viendo esta función JavaScript tan sencilla, podríamos pensar que esa función la ejecuta cualquiera, pero ¿Qué pasa si lo que queremos pasar por parámetro no es un String sino un objeto .Net llamado Mensaje con su Nombre, Fecha y Texto?
Pues serializamos el objeto.
Serializar es el proceso de codificación de un objeto con el fin de transmitirlo en red (o en este caso de Silverlight a JavaScrip) en forma de una serie de bytes (serialización binaria) , o en formato texto legible por los humanos (XML o Json).
En nuestro caso vamos a serializar en formato texto y de los formatos de texto el que menos ocupa y el más fácil de deserializar por JavaScript es Json. Json es un formato que serializa los objetos de con la misma sintaxis con la que JavaScript crea los obetos.
Así que usando JSON estamos optimizando la cantidad de datos pasados por parámetro y el rendimiento de la deserialización.
Imaginemos que estamos haciendo un chat y necesitamos pasar un objeto Mensaje entre las dos aplicaciones Silverlight. Lo primero es etiquetar la clase Mensaje con el atributo DataContract para indicar que la clase es serializable y las propiedades de la clase con el atributo DataMember por lo mismo. Estos dos atributos pertenecen al ensamblado System.Runtime.Serialization.
[DataContract]
public class Mensaje
{
[DataMember]
public string nombre { get; set; }
[DataMember]
public string fecha { get; set; }
[DataMember]
public string texto { get; set; }
}
Para serializar un objeto cualquiera en Json realizamos el siguiente método extensor (.Net Framework 3.5):
/// <summary>
/// Metodo extensor que serializa cualquier objeto en JSON
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToJson(this object obj)
{
using (MemoryStream ms = new MemoryStream())
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
serializer.WriteObject(ms, obj);
ms.Position = 0;
using (StreamReader reader = new StreamReader(ms))
{
return reader.ReadToEnd();
}
}
}
Tener en cuenta que para poder usar la clase DataContractJsonSerializer tenemos que añadir al proyecto la referencia:
System.ServiceModel.Web => using System.Runtime.Serialization.Json;
Aunque parezca raro, el using de arriba va asociado con esa referencia, me costó bastante averiguarloJ.
Hasta ahora, ya sabemos cómo llamar desde Silverlight a una función JavaScript, incluso si los parámetros de la función son objetos complejos. Vamos a ver un ejemplo:
function enviarMensajeObjeto(mensaje)
{
//El formato JSON coincide que tiene la misma sintaxis que la
//creacion de objetos en JavaScript, por eso es facil crear el //objeto simplemente usando eval.
var jsMensaje = eval('(' + mensaje + ')');
alert(mensaje);
}
Esta función JavaScript de arriba recibe como parámetro un string con el objeto Mensaje serializado en Json y para guardarse el objeto en una variable de tipo Mensaje en JavaScript solamente tiene que usar la función eval, que evalúa una cadena de texto como si de código JavaScript se tratase.
private void enviarMensaje(Mensaje msg)
{
HtmlPage.Window.Invoke("enviarMensajeObjeto", msg.ToJson());
}
La función de Silverlight es simplemente una invocación a la función JavaScript serializando la variable msg, antes de enviarla como parámetro.
El resultado al invocar a la función enviarMensaje desde Silverlight es el Mensaje en Json:
2. Invocar función C# o VB desde JavaScript
Ahora vamos a aprender lo contrario, queremos llamar desde JavaScript a una función Silverlight.
Para poder invocar una función Silverlight primero tendremos que poder referenciar al UserControl (Page) que es el objeto que tendrá el método que queremos invocar.
Para referenciar desde Silverlight el UserControl tenemos que hacer varias cosas:
a) Declarar la clase Page (UserControl) como ScriptableType y el/los métodos que queremos invocar desde JavaScript etiquetarlos con el atributo ScriptableMember:
[ScriptableType]
public partial class Page : UserControl
{
[ScriptableMember]
public void setMensaje(ScriptObject scriptPerson)
{
Mensaje mensaje = scriptPerson.ConvertTo<Mensaje>();
listaNombres.AddMensaje(mensaje);
}
//Constructor, resto de métodos, etc...
}
Nota:
Hay que hacer notar que el parámetro de la función setMensaje no es un objeto Mensaje, sino ScriptObject. Y es que todos los objetos que se vallan a pasar desde JavaScript son ScriptObject, aunque internamente tenga la misma estructura que el objeto Mensaje, de ahí que la conversión sea automática con el método ConverTo.
b) Exponer la instancia del objeto Page al navegador y darle un nombre con el que será llamado desde JavaScript (Page también).
public Page()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("Page", this);
}
Una vez hecho esto ya podemos invocar al método setMensaje desde JavaScript con la siguiente función:
function enviarMensajeS1(mensaje)
{
//Se obtiene el objeto Silverlight con getElementByID
var silverlight1 = getSilverlight1();
if(silverlight1)
{
//El formato JSON coincide que tiene la misma sintaxis que la
//creacion de objetos en JavaScript, por eso es facil crear el //objeto simplemente usando eval.
var jsMensaje = eval('(' + mensaje + ')');
//Llamada a la funcion de Silverlight 2
silverlight1.content.Page.setMensaje(jsMensaje);
}
}
Esta función será invocada desde la Aplicación Silverlight 2, se pasará como parámetro un objeto Mensaje serializado con Json, y la función creará el objeto Mensaje con eval y después llamará a la función setMensaje pasándole como parámetro el objeto creado por JavaScript que será de tipo (.Net) ScriptObject.
Como conclusión de todo este documento, cabe destacar que todo esto explicado tiene muchas utilidades: comunicar dos aplicaciones Silverlight, o comunicar una aplicación Silverlight con una Flash, o simplemente Silverlight con JavaScript.
En concreto el ejemplo que he implementado es la comunicación de dos aplicaciones Silverlight y está tanto en C# como en VB.
