Las Cosas Claras: Llamar funciones javascript desde Silverlight

Hoy veremos algo que nos puede resultar muy útil en nuestras aplicaciones Silverlight. Estamos hablando de la posibilidad de acceder al DOM (Document Object Model) del navegador para llamar a una función javascript que hará lo que queramos en el cliente.

Para ello necesitamos en primer lugar usar el siguiente Namespace:

using System.Windows.Browser;

En este namespace disponemos de la clase HtmlPage que será la que nos permita tener el acceso necesario al DOM del navegador.

Para este post he montado en un momentillo un ejemplo muy fácil. Son dos botones y una caja de texto. El primer botón llama a una función que muestra un alert con un mensaje predefinido en el cliente y el segundo botón muestra un alert con el texto del TextBox como mensaje. Algo sencillo pero que muestra lo más básico que podemos hacer: Llamar a una función con y sin parámetros.

Sltobrowser

En el código fuente podemos ver como utilizando el método GetProperty al que se le pasa el nombre de la función javascript, obtenemos un objeto de tipo ScriptObject. Este tipo de objeto dispone del método InvokeSelf que se encarga de hacer la llamada a la función en cuestión. Este método recibirá los parámetros que le serán pasados a la función javascript.

private void bttLaunch_Click(object sender, RoutedEventArgs e)
{
    ScriptObject scriptMethod = (ScriptObject)HtmlPage.Window.GetProperty("MostrarAlerta");
    scriptMethod.InvokeSelf();
}

private void bttLaunch2_Click(object sender, RoutedEventArgs e)
{
    ScriptObject scriptMethod = (ScriptObject)HtmlPage.Window.GetProperty("MostrarAlerta2");
    scriptMethod.InvokeSelf(txtMessage.Text);
}

Un muestra del resultado por ejemplo al pulsar el segundo botón seria el siguiente:

Capture

En el siguiente post de esta serie veremos como hacer lo contrario. Llamar desde el cliente, el navegador, a un función del Silverlight.

Twitter desde Silverlight Beta 2

Fácilmente! Esa es la forma en la que podemos obtener desde Silverlight Beta 2 nuestro post en Twitter. Este nos provee de una forma de obtener en diferentes formatos, nuestros últimos post y más información.

Con la siguiente URL: http://twitter.com/statuses/user_timeline/USERNAME.xml podemos obtener en formato XML los post y más información. Solo decir que debemos autenticarnos para poder recibir esta información.

Twitter provee los siguientes formatos: RSS, XML y JSON

Para más información sobre códigos de error posibles, limitaciones de uso, etc: Twitter API Documentation

En el ejemplo de hoy vamos a consumir el último mensaje del usuario de este blog FernandoBlog.

TwitterSL

Para ello crearemos en primer lugar una aplicación Silverlight hospedada en un Sitio Web también creado en el mismo momento. De la forma en la que lo haremos, crearemos un servicio WCF que expondrá un método de obtención de los mensajes del usuario. En la siguiente imagen se puede ver las clases que necesitaremos para tal servicio:

TwitterSL4

  • Service: Expone el contrato del servicio. En este caso dos métodos para obtener los mensajes del usuario identificado y otro para obtener los mensajes de un usuario a cuyos mensajes tenga acceso el usuario identificado.
  • TwitterHelper: Es la clase que realiza la petición HTTP al API de Twitter y donde se procesan los datos recibidos
private List<TwitterPost> GetRequest(string uri)
{
    WebRequest request = HttpWebRequest.Create(uri);

    if (!string.IsNullOrEmpty(_ttUser) && !string.IsNullOrEmpty(_ttPwd))
    {
        request.Credentials = new NetworkCredential(_ttUser, _ttPwd);
    }

    WebResponse res = request.GetResponse();
    StreamReader stream = new StreamReader(res.GetResponseStream());
    TextReader reader = new StringReader(stream.ReadToEnd());

    XDocument xDoc = XDocument.Load(reader);

    return (from post in xDoc.Descendants("statuses").Descendants("status")
           select new TwitterPost
           {
               Id = post.Element("id").Value,
               CreatedAt = DateTime.ParseExact(post.Element("created_at").Value, "ddd MMM dd HH:mm:ss zzzzz yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal),
               Text = HttpUtility.HtmlDecode(post.Element("text").Value)
           }).ToList<TwitterPost>();
}

  • TwitterPost: Representa un mensaje de twitter. El API devuelve mucha información que para este ejemplo solo con el Identificador, el texto del mensaje y la fecha de la creación del mismo nos es suficiente.

 

En la parte de Silverlight simplemente añadiremos una referencia la servicio WCF creado y haremos una llamada al método correspondiente.

public Page()
{
    InitializeComponent();
    ServiceClient client = new ServiceClient();
    client.TwitterPostsCompleted += new EventHandler<TwitterPostsCompletedEventArgs>(client_TwitterPostsCompleted);
    client.TwitterPostsAsync();
}

private void client_TwitterPostsCompleted(object sender, TwitterPostsCompletedEventArgs e)
{
    List<TwitterPost> twitter = e.Result;
    if (twitter.Count > 0)
    {
        TwitterPost lastTwitter = (from post in twitter orderby post.CreatedAt descending select post).First();
        txtPost.Text = lastTwitter.Text;
        txtTime.Text = RelativeCreatedAt(lastTwitter.CreatedAt);
    }
}

Como curiosidad se muestra la función utilizada para mostrar la fecha de creación del mensaje en relación al día en la que se obtiene el mensaje desde Silverlight:

private string RelativeCreatedAt(DateTime _createAt)
{
    TimeSpan _time = DateTime.Now.Subtract(_createAt);

    int _days = Convert.ToInt32(_time.TotalDays);
    if (_days > 0) return string.Format("Hace {0} dia{1}", _days, _days > 1 ? "s" : "");

    int _hours = Convert.ToInt32(_time.TotalHours);
    if (_hours > 0) return string.Format("Hace {0} hora{1}", _hours, _hours > 1 ? "s" : "");

    int _minutes = Convert.ToInt32(_time.TotalMinutes);
    if (_minutes > 0) return string.Format("Hace {0} minuto{1}", _minutes, _minutes > 1 ? "s" : "");

    int _seconds = Convert.ToInt32(_time.TotalSeconds);
    if (_seconds < 0) _seconds = 0;
    return string.Format("Hace {0} segundo{1}", _seconds, (_seconds > 1 || _seconds == 0) ? "s" : "");
}

Finalmente el resultado sería el siguiente.

TwitterSL2

ASP.NET Development Server: Port Number Error

El otro día conseguí resolver el problema que comenté en el post Internet-Explorer cannot display the webpage y cerré el post comprobando que cuando intento depurar un sitio web, el puerto en el que escucha ASP.NET Development Server luego no es el que intenta acceder el IE7.

En la siguiente imagen se aprecia el ASP.NET Development Server levantado en el puerto

Asp.netDevelopmentServer

Y vemos como se lanza el IE7 intentando acceder al puerto

Asp.netDevelopmentServer2

Yo en el anterior post pensaba en un primer momento que podía deberse al antivirus que tengo ESET Nod3d Antivirus, pero finalmente no fue así. Sin embargo, esto si que huele a antivirus. Investigando un poco, encontré que se puede excluir el ASP.NET Development Server de ser monitorizado por el antivirus.

Asp.netDevelopmentServer3

Asp.netDevelopmentServer4

Por tanto, desmarcando de la sección Web browsers y Active mode el ejecutable de ASP.NET Development Server, podremos por fin tener un comportamiento normal en nuestro entorno de desarrollo.

Silverlight Beta2: Crossdomain

Hasta la salida de la Beta 2 de Silverlight, cuando queríamos con la Beta 1 realizar una llamada un Servicio Web o WCF sabíamos que debíamos de colorar los ficheros Crossdomain.xml o ClientAccessPolicy.xml en el raíz del Sitio Web donde estuviese el Servicio a consumir por Silverlight. Silverlight por defecto irá a buscar el fichero ClientAccessPolicy.xml y sino lo encuentra buscará el Crossdomain.xml

Si hacemos pruebas desde Silverlight Beta2, nos encontraremos que aun teniendo estos ficheros, recibimos el siguiente error depurando:

SLBeta2WS

El formato del fichero ClientAccessPolicy.xml que teníamos hasta ahora con el que conseguíamos que funcionase era:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Pero para esta versión necesitamos realizar un sutil cambio en el fichero:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Ahora si que funciona nuestro Silverlight.

Internet Explorer cannot display the webpage

Vamos por partes. Máquina con Windows Vista y Microsoft Visual Studio 2008 recién instalado. Creamos un nuevo sitio web y cuando queremos depurar, nos encontramos con esto:

NoEncuentralocalhost

¿Qué será?….El problema esta en el puñetero IPv6, y ¿Cómo deshabilitar esto?

Abrimos Regedit.exe y en HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicestcpip6Parameters añadimos un DWORD de nombre DisabledComponents y asignamos el valor decimal 255.

Después de reiniciar he conseguido que funcione, a pesar de que aunque el ASP.NET Development Server levanta un puerto, en el IE 7 intenta acceder a otro!!

Otro día busco porqué!

Movimiento Circular: Senos y Cosenos

¿Como nos pueden ayudar los Senos y Cosenos en nuestras aplicaciones Silverlight o Windows Presentation Foundation?

Senos y Cosenos

Imaginemos que tenemos una elipse y queremos que realice un movimiento circular. Utilizando las funciones trigonométricas Seno y Coseno de un ángulo dado podemos obtener el punto X e Y de la circunferencia imaginaria por donde queremos mover nuestra elipse.

Un ejemplo sencillo y que tal vez no tenga mucho sentido, pero que se puede ver el concepto del que estamos hablando: