Categoría: Tutoriales

La palabra clave yield

La semana pasada el maestro Eduard Tomás propuso la solución del reto MSDN utilizando la palabra clave yield en un método para evitar el uso de una colección adicional.

¿Qué se esconde tras esta palabra clave?

El patrón Iterator

Si has escrito código C# alguna vez, ya conocerás las interfaces IEnumerable e IEnumerable<T> que permiten recorrer fácilmente una colección con la palabra clave foreach:

IEnumerable<int> list=new int[]{1,2,3,4};
//...
foreach (var value in list)
{
    //do something
}

Como veis en el ejemplo, este interfaz lo implementan todas las colecciones desde los tipos más básicos como el Array (aunque de una forma un tanto particular).

El interfaz sólo tiene un método, GetEnumerator que devuelve un IEnumerator, que nos devolverá una clase que es la que realmente sabe iterar sobre nuestra colección de elementos. Es el típico patrón Iterator del GoF (Design Patterns: Elements of Reusable Object-Oriented Software).

Sigue leyendo

Anuncios

Migrando el tres en raya: Azure SDK 2.4, SignalR 2.1.1, Windows 8.1

El año pasado escribí una serie de tres artículos donde explicaba cómo crear un juego de tres en raya para jugar online en tiempo real, desde el navegador y también desde una app Windows 8. Como la tecnología avanza muy rápido, al cabo de un mes de escribir el artículo el código ya no compilaba con los últimos SDK. Así que, ahora que estoy de vacaciones me he dedicado a procrastinar y a actualizar alguno de mis proyectos personales para no tener que hacer lo que me había propuesto :). Aquí os dejo los pasos a realizar para migrar, aunque podéis descargar directamente el nuevo paquete aquí.

Como primer paso, conviene tener Visual Studio 2013.3 instalado y actualizar el SDK de Windows Azure al último disponible (2.4 por ahora).
Sigue leyendo

Tareas de fondo en aplicaciones universales

Hace unos días @johnnyjosep me preguntó cómo podía hacer en una aplicación universal para descargar nuevo contenido cada cierto tiempo sin tener la aplicación abierta.

Sí, os debo la serie de artículos sobre el caché de HTTP, aunque si os fijáis bien en este hay un pequeño ejemplo de uso de la cabecera If-Modified-Since.

La forma más eficiente de notificar a la aplicación que algo remoto ha cambiado, es utilizar un sistema de notificaciones push, estilo Azure Notification Hubs, pero como esa opción tiene un coste, también tenemos otra posibilidad más económica (para nosotros): crear una Background Task con un temporizador, que compruebe desde el cliente si el archivo ha cambiado.

Sigue leyendo

Usar SQLite en Apps Windows 8.1

Cuando necesitamos guardar datos estructurados que acostumbramos a tener en una base de datos, tenemos unas cuantas alternativas, entre ellas usar SQLite, una librería que incluye un motor SQL sobre un archivo del disco, con relaciones, índices, transacciones e incluso consultas estilo LINQ.

SQLite es una librería nativa, pero gracias a Visual Studio, NuGet y a los desarrolladores de la librería, será muy fácil integrarlo en nuestra aplicación.

Cómo agregar SQLite en nuestra App

El primer paso será descargar el paquete de librerías preparado para Windows Runtime 8.1 de la página de descargas de SQLite.
Debemos buscar el paquete para WinRT 8.1 en la sección Precompiled Binaries for Windows Runtime. Es un archivo .VSIX que podremos instalar como paquete en el Visual Studio.
La versión que he utilizado para este artículo es la 3.8.2 que podéis descargar aquí.

SQLite VSIX

Una vez instalado, en nuestro proyecto C# o VB.Net de la Tienda Windows, añadiremos la referencia a la librería SQLite. Podemos encontrarla en Windows>Extensions:

SQLite Windows Runtime Libraries

Este paquete nos incluirá la librería SQLite 3, pero esta librería es nativa y no se puede utilizar directamente desde C# o VB.Net. Para empezar a usarla necesitaremos una clase que encapsule las llamadas. Por suerte ya está hecha, basta descargar sqlite-net a través de NuGet:

sqlite-net NuGet Package

Este paquete instala dos archivos c# que nos hacen de puente con la librería y nos permiten ejecutar consultas contra una base de datos sqlite:
sqlite.cs

Ejemplo de uso

Ya sólo nos queda crear una base de datos, empezar a grabar y a consultar datos. Como es un archivo local, tendremos que copiar una .db existente o bien crear una dentro del sistema de archivos de la aplicación (nos sirven tanto la carpeta temporal como la local). Aquí vemos un pequeño ejemplo de definición, creación e inserción.

public class Person{
    [SQLite.PrimaryKey,SQLite.AutoIncrement]
    public int Id { get; set; }

    [SQLite.MaxLength(50)]
    public string Name { get; set; }

    [SQLite.MaxLength(50)]
    public string Surname { get; set; }

}

private async void createDB()
{
    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var conn = new SQLite.SQLiteAsyncConnection(
        System.IO.Path.Combine( folder.Path,"test.db"));
    
    await conn.CreateTableAsync<Person>();            
}

private async Task insertPersonAsync(string name, string surname)
{
    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var conn = new SQLite.SQLiteAsyncConnection(
        System.IO.Path.Combine(folder.Path, "test.db"))
    await conn.InsertAsync(new Person { Name = name, Surname = surname });
}

Esto no es lo que parece, el “this” en JavaScript

Leandro's Pool

JavaScript es un lenguaje muy dinámico, tanto que lo podemos utilizar como lenguaje orientado a objetos, como lenguaje funcional y redefinir una función de sistema, todo en la misma línea sin despeinarnos. Cuando estás acostumbrado a lenguajes orientados a objetos “clásicos”, algunas características de JavaScript se te hacen extrañas y pueden traerte más de un dolor de cabeza. Por eso es importante conocer bien el lenguaje en el que estás programando y no sólo algunas librerías útiles.

He visto bastante gente que lleva algún tiempo desarrollando en JavaScript y todavía no tiene claros algunos conceptos básicos del lenguaje, así que aquí va un artículo para intentar aclarar estos puntos; tampoco soy un experto en JavaScript, pero tener claros estos conceptos me ha ayudado mucho durante el desarrollo de algunas aplicaciones de Windows 8 y me ha evitado hacerme un lío con lo que ya sabía de C# y Java. Si vais a desarrollar una aplicación JavaScript moderna es necesario conocer cómo se escribe el JavaScript de hoy.

Las dos características que suelen despistar más al principio son: el ámbito de las variables y la palabra reservada this. Como es necesario comprender el primero para utilizar bien el segundo, vamos por orden.

Ámbito de variables

En JavaScript tenemos dos ámbitos para las variables:

  • El Global, visible desde toda la aplicación. En este contexto deberíamos evitar declarar variables y funciones, pues es muy fácil equivocarse y reescribir su valor, cambiando la funcionalidad de la aplicación por completo, aunque esto también se puede aprovechar a la hora de ofrecer objetos en una API. La mayoría de frameworks declaran dentro del ámbito global algunos objetos básicos, pero, para evitar conflictos con otras librerías, suelen utilizar una técnica de espacios de nombres.
  • El  Local es a nivel de función, todas las variables que se declaran dentro de una función no son visibles fuera de la misma, pero si son visibles a las funciones que declaremos internamente. Este pequeño truco nos vendrá muy bien para poder utilizar el concepto de closures.

Sin profundizar demasiado, hagamos un repaso de los efectos del ámbito en las variables. En el siguiente fragmento de código, vemos como la variable a es definida en el ámbito global y desde la función b podemos cambiar su valor.

var a=0;
function b(){
    a=5;
}

b();
log("ejecución b: "+a);

El resultado tras ejecutar b() será 5, hemos modificado el valor de la variable global. De hecho podríamos cambiar cualquier cosa de la variable, incluso asignarle el no-valor undefined. No probéis esto sin que os acompañe un adulto ;).

Si definimos una variable dentro de una función sólo es visible dentro de la misma y no entra en conflicto con las definiciones globales:

function c(){
    var a=10;
}
c();
log("ejecución c: "+a);

En este punto (supongamos que va a continuación del código anterior), el valor global de a no habrá cambiado y seguiremos teniendo el valor 5.

Las variables locales a una función se pueden declarar en casi cualquier lugar, por ejemplo dentro de un bloque if, dentro de un bucle, dentro de un bloque de llaves; el siguiente bloque de código nos muestra cómo el código tiene acceso a una variable definida dentro de un bucle, JavaScript no nos pondrá ningún problema, ni siquiera con la clausula “use strict”:

for(var i=0;i<50;i++){
    var x=i;
}
log("valor de x tras el bucle: "+x);

El resultado será:

valor de x tras el bucle: 49

Cuando tenemos una variable con ámbito de función también es visible a las funciones definidas dentro de la misma. Así:

function d(valor){
    function cuadrado(){
        return valor*valor;
    }
    return cuadrado();
}

log("ejecución d:"+d(a));

La función que definimos dentro de d no necesita que le enviemos el parámetro, pues puede acceder al mismo directamente.

Podemos tener efectos secundarios que no voy a explicar aquí, pero seguro que os parece interesante el artículo de Robert Nyman.

Debido a todo esto y con el objetivo de mantener el ámbito global lo más limpio posible, habréis visto en más de una ocasión la siguiente estructura o una de sus variantes:

(function(){
 var miVariable="incalculable";
 function miFuncion(){
  console.log("Mi variable tiene un valor " + miVariable);
 }
 miFuncion();
})();

Este código declara una función y dentro de la misma declara una variable, una función y llama a ésta última. La parte interesante de esta función es que está declarada entre paréntesis y tiene otro par de paréntesis al final.
Los primeros paréntesis convierten a la función en una expresión, al ser una expresión no crea un nombre de función, aunque lo pongamos, en el ámbito global y lo único que podemos hacer con la misma es ejecutarla, o asignarla a una variable para poder ejecutarla más tarde.
Los segundos paréntesis hacen esto mismo, ejecutar la expresión. Como la expresión es una función, todas las variables declaradas dentro se quedarán en el ámbito de la función y así evitamos que cualquier otro código pueda entrar en conflicto con el nuestro.
 

La palabra reservada this y el ámbito de ejecución

Cuando hablamos de orientación a objetos, habitualmente tenemos una manera de acceder a la instancia propietaria de la función que se está ejecutando con alguna clave como this o self. Es por eso que cuando vemos que JavaScript tiene la palabra clave this pensamos que lo tenemos todo controlado y nos creamos un objeto que utiliza this para todo:

function objeto(v){
    this.valor=v;
    this.desplaza=function(cantidad){
                log(this.valor+" + "+cantidad+" = "+  (this.valor+cantidad));
                log("");
            }
}

Después, llamamos a nuestra función desplaza perteneciente al objeto y parece que funciona…

var o=new objeto(5);
//llama a la función definida en el objeto
log("o.desplaza(10) >>>");
o.desplaza(10);

Y el resultado es:

o.desplaza(10) >>>
5 + 10 = 15

Como sabréis, acceder a propiedades de objetos en JavaScript es considerablemente más lento que el acceso a una variable, así que un día decidimos optimizar nuestro código guardando la llamada en una variable. Al cabo de unas horas de depuración nos volvemos a dar cuenta que JavaScript, además de orientado a objetos, es un lenguaje dinámico y funcional:

//llama a la función definida en el objeto, pero cambia el contexto al asignar
//la función a una variable
log("var desp=objeto.desplaza >>>");
var desp=objeto.desplaza;
desp(5);

El resultado puede ser mucho más alucinante si alguien ha metido algo con el mismo nombre dentro del objeto global (window):

//llama a la función definida del objeto, pero utiliza el objeto global como this
log("window.valor=100 >>>");
window.valor=100;
var desp=o.desplaza;
desp(5);

¿Qué ha pasado aquí? Al colocar la función en una variable hemos cambiado el ámbito de la llamada y ahora se ejecuta en el global (window en un navegador). Para evitar esto, habitualmente se utiliza el concepto de closure para empaquetar el this y que sea visible al código que se ejecuta.

//redefinición del objeto, guardando el this...
function objetoMejor(v){
    var that=this;
    this.valor=v;
    this.desplaza=function(cantidad){
                log(that.valor+" + "+cantidad+" = "+  (that.valor+cantidad));
                log("");
            }
}
var o2=new objetoMejor(5);
//llama a la función definida en el objeto
log("o2.desplaza(10) >>>");
o2.desplaza(10);
log("var desp=o2.desplaza >>>");
var desp2=o2.desplaza;
desp2(10);

Pero este método nos obliga a definir todo el objeto en el constructor; en ocasiones eso no nos vendrá bien, pero no todo está perdido.

Dominando el ámbito de this con call, apply y bind

Además del método pedestre that=this, JavaScript cuenta con tres funciones que nos ayudarán a dominar el ámbito de ejecución como nosotros queramos. Las llamadas call,apply y bind.

Las dos primeras funciones nos servirán para inyectar el ámbito en la llamada que nosotros queramos.

//llama a la función definida en el objeto, pero cambia el valor de this
log("o.desplaza.apply({valor:10},[5]) >>>");
o.desplaza.apply({valor:10},[5]);
log("o.desplaza.call({valor:10}5) >>>");
o.desplaza.call({valor:10},5);

En ambos casos, el primer parámetro es el objeto que queremos que haga de this en la llamada, podemos poner el propio objeto (o) o bien inyectarle el que nosotros queramos, en el ejemplo un nuevo objeto anónimo con un valor. La única diferencia entre las dos funciones es que la primera recibe un array como parámetro, la segunda utiliza todos los parámetros que le pasemos a partir del segundo.

Estas funciones nos pueden ser útiles, pero la que más me gusta es la función bind, que en lugar de ejecutar la llamada, nos devuelve una nueva función pero con el valor que nosotros le pasamos como this.

function objetoMejorMasClaro(v){
    this.valor=v;
    this.desplaza=function(cantidad){
                log(this.valor+" + "+cantidad+" = "+  (this.valor+cantidad));
                log("");
            }.bind(this);
}
var o3=new objetoMejorMasClaro(5);
log("o3.desplaza>>>");
o3.desplaza(10);
var d3=o3.desplaza;
d3(10);

Como he dicho en el párrafo anterior, bind utiliza el valor. Esto lo hace muy diferente de una closure pues en esta última se toma la variable. Observad la diferencia de resultados:

var fclosure, fbind;
(function(){
    var x=5;
    fclosure=function (){
        return x*x;
    }
    
    function cuadrado(){
        return this*this;
    }
    fbind=cuadrado.bind(x);
    
    x=3;
    
})();

log("cuadrado closure="+fclosure());
log("cuadrado bind="+fbind());

Los resultados son:

cuadrado closure=9
cuadrado bind=25

Así que tened cuidado con el bind!

El bind también nos será muy útil cuando definimos funciones de objetos, funciones dentro de prototype y cuando enviemos funciones a parámetros de callback, como en la ejecución de un setTimeout:

log("setTimeout sin bind y con bind >>>");
setTimeout(o.desplaza,500,5);
setTimeout(o.desplaza.bind(o),600,15);

Espero que os haya servido para aclarar algunos conceptos. Como lo mejor para conocerlos bien es jugar con el código, aquí os dejo un jsfiddle: http://jsfiddle.net/jmservera/WNWaM/

¡Disfrutad!

Las nuevas características de búsqueda en Windows 8.1

Windows 8.1 Preview presenta grandes novedades con respecto a la versión actual. Una que me ha chocado mucho es el nuevo sistema de búsqueda. Tiene cosas muy buenas y otras no tanto, pero tengamos en cuenta que todavía es una versión preliminar y puede cambiar mucho hasta el lanzamiento.

found dog

Desde el punto de vista del desarrollador, en la versión anterior era muy fácil y directo integrarse con el contrato de búsqueda; permitía a los usuarios buscar dentro de nuestra aplicación aunque ésta estuviera cerrada, haciendo que nuestra aplicación estuviera siempre presente.

Cookbook search old

A pesar de los esfuerzos de Microsoft por explicar las bondades del sistema de búsqueda, en el mundo real™ los usuarios no acababan de entender el sistema o simplemente no encontraban el botón de búsqueda dentro de la aplicación y pensaban que la aplicación no podía buscar. En las aplicaciones no había ninguna evidencia de que pudieran buscar y al final poníamos un botón dentro de la aplicación que abría el charm de búsqueda. Además, cuando empiezas a tener muchas aplicaciones que implementan el contrato de búsqueda la lista de posibilidades se vuelve demasiado larga para ser manejable.

Por este motivo y porque la funcionalidad del búsqueda dentro de cualquier aplicación sólo la utilizaba el 7% de los usuarios, el equipo de Windows decidió cambiar el sistema de búsqueda para mejorarlo y hacerlo más eficiente.

¿Dónde están las Apps?

El nuevo charm de búsqueda ya no muestra una lista de aplicaciones disponibles. En su lugar nos permite buscar dentro de todo el sistema, integrando los resultados del contenido indexado en nuestro equipo con otros datos devueltos por Bing, Xbox Music y otros.
Las aplicaciones antiguas que implementan el contrato de búsqueda de Windows 8 no perderán esa funcionalidad; podremos seleccionar la aplicación para que nos devuelva la página de resultados, pero sólo nos aparecerá esta opción cuando sea la aplicación activa y no estará seleccionada por defecto, pero esto último se puede cambiar.

cookbok search 0

Una vez seleccionada nuestra aplicación en lugar del “Search everywhere” podremos seguir usándola como antes, aunque no tendremos la posibilidad de cambiar a otra aplicación:

Cookbok search

¿Cómo haremos para que se pueda buscar dentro de nuestra aplicación desde cualquier sitio?

Está algo más escondido para nosotros, pero en realidad será mejor que antes, pues parece ser que podremos ofrecer resultados dentro de la búsqueda general. Todavía no está disponible en la preview que podemos probar ahora, si bien en esta charla del Build podemos ver cómo será.
Además, la funcionalidad “Search everywhere” permite encontrar nuestra aplicación si el usuario la tiene instalada y, como me suele ocurrir mucho, no recuerda exactamente el nombre:

search everywhere

¿Y cómo implemento ahora las búsquedas?

Podemos optar por seguir haciéndolo como antes o bien utilizar los nuevos controles y capacidades de búsqueda.

Charm de búsqueda

En el caso de que queramos seguir con la implementación antigua, lo recomendación es que pongamos un botón en la zona superior derecha que abra el charm de búsqueda, así nuestros usuarios sabrán que nuestra aplicación puede buscar. Al ejecutar el siguiente código, el charm estará enfocado en nuestra aplicación en lugar de en el “Search everywhere”, e incluso podemos poner un término de búsqueda por defecto:

Windows.ApplicationModel.Search.SearchPane.getForCurrentView().show("buscar...");

Todavía tenemos una opción mejor, hacer que el cuadro de búsqueda aparezca al empezar a escribir, tal como hacen las aplicaciones de calidad y la pantalla de inicio. El código para añadir esta funcionalidad es muy sencillo:

Windows.ApplicationModel.Search.SearchPane.getForCurrentView().showOnKeyboardInput = true;

El control SearchBox

Si queremos ajustarnos a las nuevas recomendaciones de búsqueda, el equipo de Windows nos lo ha puesto muy fácil. Tenemos un nuevo control de búsqueda que tiene eventos muy parecidos al contrato de búsqueda, pero es un control visual que podemos poner en nuestro UI, tanto para XAML como para HTML:

<div class="pagesearch" data-win-control="WinJS.UI.SearchBox"></div>

Una vez en nuestra página, podremos usar el código que utilizábamos antes en nuestro contrato de búsqueda que utilizaba la consulta:

var search = element.querySelector(".pagesearch").winControl;
search.onquerysubmitted = function (e) {
    console.log(e.detail.queryText);
};

Sólo que ahora tendremos que navegar nosotros a la página de resultados.

También podemos añadir sugerencias de búsqueda:

search.onsuggestionsrequested = function (e) {
    var query = e.detail.queryText.toLowerCase();
    var suggestionCollection = e.detail.searchSuggestionCollection;
    var suggestionList = ["Almería","Albacete", "Alicante"];
    for (var i = 0, len = suggestionList.length; i < len; i++) {
        if (suggestionList[i].substr(0, query.length).toLowerCase() === query) {
            suggestionCollection.appendQuerySuggestion(suggestionList[i]);
        }
    }
};

Activar el foco del control al usar el teclado en la aplicación:

search.winControl.focusOnKeyboardInput = true;

Y ofrecer resultados recomendados con imágenes y separadores para identificarlos del resto, lo que dará un toque de calidad a nuestra aplicación:

search.onsuggestionsrequested = function (e) {

    var queryText = e.detail.queryText,
        query = queryText.toLowerCase(),
        suggestionCollection = e.detail.searchSuggestionCollection;
    var suggestionList = [{
        name: "Almería",
        thumb: "http://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Bandera_de_Almer%C3%ADa.svg/200px-Bandera_de_Almer%C3%ADa.svg.png"
    },
        {
            name: "Albacete",
            thumb: "http://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Albacete-Bandera.svg/200px-Albacete-Bandera.svg.png"
        },
        {
            name: "Alicante",
            thumb: "http://upload.wikimedia.org/wikipedia/commons/thumb/2/29/Flag_of_Alicante.svg/200px-Flag_of_Alicante.svg.png"
        }];

    for (var i = 0, len = suggestionList.length; i < len; i++) {
        if (suggestionList[i].name.substr(0, query.length).toLowerCase() === query) {
            if (suggestionCollection.size <2) {
                var reference = Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(
                    new Windows.Foundation.Uri(suggestionList[i].thumb));
                suggestionCollection.appendResultSuggestion(suggestionList[i].name,
                    "El mejor resultado!", "id1", reference, "mejor resultado");
            }
            else {
                if (suggestionCollection.size == 2)
                    suggestionCollection.appendSearchSeparator("Más sugerencias");
                suggestionCollection.appendQuerySuggestion(suggestionList[i].name);
            }
        }
    }
};

sugerencias

Por lo que podemos ver es un control muy completo, que además cachea las últimas búsquedas para ofrecerlas también como sugerencia.

El indexador de Windows

Además del nuevo control de búsqueda, podemos añadir y consultar elementos con el indexador de Windows mediante la clase ContentIndexer. Antes de que os frotéis las manos como hice yo, leed la explicación que aparece en la documentación:

Content is separated per app package, per user, and a package can’t query another package’s data.

Podemos utilizar la potencia del indexador de Windows, pero sólo para y por nuestra aplicación, los datos que añadamos al índice son privados para nuestra aplicación y usuario.

Dicho esto,

  • La clase ContentIndexer nos permite añadir elementos al índice de Windows, de manera que podremos aprovechar la potencia del indexador de Windows dentro de nuestra aplicación. Por ahora el contenido que añadamos al índice será sólo para nuestra aplicación, el resto de las aplicaciones no pueden verlo.
  • También podemos añadir contenido indexado utilizando carpetas locales y creando ficheros appcontent-ms.
  • Con las clases del espacio de nombres Windows.Storage.Search podremos realizar búsquedas dentro del contenido indexado utilizando Advanced Query Syntax, pero sólo del contenido propio.
  • Todavía no sabemos cómo podremos hacer para aparecer dentro de las búsquedas generales de Windows Search.

Conclusiones

Tras estudiar el uso que hacían de la búsqueda los usuarios de las aplicaciones de la tienda Windows, el equipo de Windows se dio cuenta que sistema necesitaba un cambio. Este cambio ha tenido algunas consecuencias pero tenemos la promesa de que va a mejorar. Mientras tanto, ya podemos aprovechar la potencia que nos proporcionan los nuevos controles y funcionalidades del sistema de búsqueda.

Referencias

Juegos sociales online con SignalR y Windows Azure (3 de 3)

Eight!
En los artículos anteriores de esta serie hemos visto cómo utilizar SignalR para proporcionar un canal de comunicaciones desde el servidor hacia el cliente. Además, modificamos el código para funcionar en Azure para mejorar la escalabilidad de nuestro servicio. Llegamos al final de la serie con un reto que me he puesto a mi mismo, utilizar lo que hemos hecho desde una aplicación de Windows 8 y que no se nos complique el artículo.

SignalR tiene librerías cliente tanto para .Net como JavaScript. Como os prometí un artículo sencillo, utilizaremos JavaScript, porque así aprovecharemos el código que ya tenemos de forma casi directa dentro de nuestra aplicación.

App de tres en raya

Como sabéis, Windows 8 te permite utilizar librerías estándar como jQuery, lo más probable es que lo que hicimos en la versión web nos funcione con muy pocos cambios. Así que… ¡Manos a la obra!
Empezamos creando una aplicación de la tienda Windows en HTML5/JavaScript:

3er.30.w8project

Para conectarnos a nuestro Hub necesitaremos las librerías cliente de SignalR. De nuevo Nuget nos ayudará a instalarlas:

3er.30.w8jsclient

HTML

El código HTML será casi el mismo que el que hicimos en nuestro default.html, sólo cambiaremos una cosa, pues no podemos utilizar la función prompt para pedir el nombre de usuario; mientras no tengamos un login, ponemos una caja de texto en pantalla. Recordad que estoy enfocando los ejemplos al uso básico de SignalR, conectarnos al sistema de autentificación ya lo haremos en otro momento:

<body>
    <label for="userName">Nombre del jugador: </label><input type="text" id="userName" />
    <div id="partida">        
...

Cliente del Hub

SignalR nos permite definir manualmente las llamadas al Hub directamente, pero también genera automáticamente un proxy con el código para nosotros en http://127.0.0.1/signalr/hubs. Si abrimos esta dirección con el Internet Explorer intentará descargarse el archivo JavaScript autogenerado:
descargarhub

Nos guardarmos el archivo en la carpeta js de nuestro proyecto para poder referenciarlo desde la página default.html. Cuando abramos el script veremos que en la línea donde se define el signalR.hub se utiliza un path relativo. Nos bastará cambiar el path por uno absoluto que apunte a nuestro servidor de Azure. Como por ahora seguimos usando el emulador, apuntamos a la dirección de bucle local 127.0.0.1:

signalR.hub = $.hubConnection("http://127.0.0.1/signalr", { useDefaultPath: false });

El script tresenraya.js también lo copiamos a la carpeta js y modificamos la llamada por la lectura en el campo username que hemos creado antes:

//prompt("Escribe tu nombre");
$username.change(null, function (e) {
    nombre = e.target.value;
});

Definimos la variable $username junto a las otras:

$username = $("#userName");

Y ya casi lo tenemos, sólo nos queda enlazar a los scripts y cambiar el tema a light para que se vea parecido a nuestra página. La etiqueta head de la página default.html de nuestra app queda como esta:

<head>
    <meta charset="utf-8" />
    <title>TresEnRapp</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.1.0/css/ui-light.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.1.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.1.0/js/ui.js"></script>

    <!-- TresEnRapp references -->
    <link href="/css/default.css" rel="stylesheet" />

    <script src="Scripts/jquery-1.6.4.min.js"></script>
    <script src="Scripts/jquery.signalR-1.1.2.min.js"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="/js/hub.js"></script>
    <script src="js/tresenraya.js"></script>
    <script src="/js/default.js"></script>

</head>

Y aquí tenéis la aplicación en Windows 8 jugando contra otra en IE10:

3er.24.sidebysidew8

Forzar el uso de WebSockets

Nuestra aplicación Windows 8 ha funcionado bien, pero si miramos la salida de la consola de JavaScript veremos los siguientes mensajes:
3er.25.websockets
Tenemos dos mensajes de error. El primero proviene de una comprobación que hace jQuery para ajustarse a las capacidades de cada navegador. El segundo debe preocuparnos un poco más, nuestra aplicación no está utilizando WebSockets y podría hacerlo.
El mensaje nos lo dice claro, no hemos habilitado las llamadas cross-domain en SignalR, aunque ha sido lo suficientemente listo como para encontrar otra manera de funcionar sin WebSockets. Antes no necesitábamos habilitar las llamadas cross-domain pues el código se ejecutaba en la página proveniente del sitio, pero ahora hemos creado el front-end dentro de una app de la tienda Windows, así que la llamada es cross-domain. Para habilitarlo sólo tenemos que configurar SignalR antes de arrancarlo en el Global.asax.cs:

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableCrossDomain = true;
// Register the default hubs route: ~/signalr
RouteTable.Routes.MapHubs(hubConfiguration);

Al ejecutar de nuevo la aplicación ya habrá desaparecido el error y estaremos aprovechando todo el potencial que nos proporciona Windows 8.

¿Y ahora qué?

Tal como os prometí, hacer que nuestra app funcionara no ha sido complicado. Crear una aplicación válida para la tienda Windows y que además tenga éxito ya es otra historia.
Ahora es vuestro turno. Nos quedan, entre otras cosas, las siguientes tareas:

  • Autentificación: podemos utilizar los sistemas estándar de autentificación de usuarios y luego aplicar el atributo Authorize para permitir o denegar el acceso a métodos y servicios.
  • Control de conexión y desconexión de usuarios: SignalR nos avisa cuando ocurre una conexión/desconexión/reconexión de usuario, nosotros “sólo” tendremos que gestionar esos cambios de estado para saber si un usuario está online o no.
  • Windows 8: esta aplicación es sólo una prueba de concepto. Hacer que nuestra aplicación brille nos costará un poco más, para conseguirlo tenemos grandes consejos en el MSDN

Espero que os haya gustado. Podéis descargar el código completo del ejemplo en el siguiente enlace de Codeplex.

Juegos sociales online con SignalR y Windows Azure (2 de 3)

Clouds
En el artículo anterior vimos cómo utilizar SignalR para crear servicios con un canal de comunicaciones abierto con el cliente. Recordad que es siempre el cliente el que establece la comunicación y luego el servidor utiliza la técnica más adecuada en cada caso para mantener el canal abierto.

El ejemplo, aunque con bastante código, no deja de ser un caso básico y con poca escalabilidad. Si queremos dar servicio a miles o millones de usuarios simultáneos, necesitaremos que nuestro servicio pueda crecer a lo ancho y no a lo alto. En lugar de aumentar potencia de CPU y RAM a una sola máquina, nos permite ir añadiendo más máquinas a medida que las necesitemos, que trabajarán en paralelo y nos permitirán un número de usuarios simultáneos sin límite.
Con Windows Azure podemos hacer esto y mucho más. Para que nuestra aplicación funcione en un entorno Cloud realizaremos algunos cambios en la misma:

  • Hasta ahora estábamos almacenando las partidas en memoria, en una aplicación en la nube no podemos utilizar esta técnica porque trabajaremos con múltiples instancias y cada una tiene su propia memoria. Necesitamos un lugar de almacenamiento que puedan compartir las diferentes instancias. En Azure tenemos diferentes posibilidades: Windows Azure SQL para datos relacionales, Windows Azure Table Storage para datos no relacionales (NoSQL), o Windows Azure Caching si necesitamos algo pequeño y muy rápido. En nuestro ejemplo usaremos las tablas de Windows Azure Storage.
  • Añadiremos un enlace al ServiceBus, de manera que cada vez que uno de los servidores necesite enviar información a todos los clientes conectados al servicio pueda avisar al resto de servidores del cluster para que también envíen esa información a sus clientes.

Qué necesito

Os recuerdo que para este ejercicio necesitamos:

Convertir un proyecto web en proyecto Azure

El primer paso es añadir a nuestra solución un proyecto de Azure que nos configurará el paquete de despliegue en la nube. Pulsamos el botón derecho sobre el proyecto y nos aparecerá la opción Add Windows Azure Cloud Service Project:

3er.11.AddCloudService

Si ejecutamos nuestra aplicación ahora, el Visual Studio arrancará el emulador de Windows Azure y nuestra aplicación se ejecutará en el entorno simulado. Al principio nos parecerá que funciona todo, para comprobar que en realidad nos va a fallar todo nos basta con configurar el rol para que se ejecuten dos instancias.

En el proyecto TresEnRaya.Azure, abrimos la carpeta Roles y hacemos doble-click en nuestro rol TresEnRaya, en la configuración podemos cambiar el número de instancias:
3er.11.increasinstances

Al incrementar el número de instancias, haremos que cada nueva conexión vaya a una máquina distinta, es decir, se irán balanceando las conexiones. Como os he comentado antes, las instancias no comparten memoria ni cpu, son instancias completamente independientes, incluso en el simulador. Con nuestro diseño de aplicación con listas en memoria nos encontramos con un problema importante: cada instancia tiene su lista de usuarios, solicitudes y partidas y no se ven entre ellas. Como podemos ver en la siguiente imagen, el botón para jugar contra “Manolo” debería estar en dos de los navegadores y sólo aparece en uno:
3er.11.ymanolo

Windows Azure Storage

Para almacenar las partidas y las solicitudes y que todas las instancias de nuestro servicio tengan acceso vamos a utilizar el Azure Table Service del Windows Azure Storage.
Utilizaremos el Storage en lugar de SQL Azure porque la sencillez de los datos nos lo permite y es un almacenamiento mucho más económico que el SQL.

Una explicación rápida: las tablas de Windows Azure son listas organizadas por clave, pueden contener hasta 252 valores en cada registro y tienen un límite de 100TB. Los puntos clave de las tablas en Azure que vamos a encontrarnos durante el desarrollo de esta aplicación son:

  • Los elementos se identifican mediante una clave compuesta por dos elementos PartitionKey y RowKey.
  • La información está agrupada por PartitionKey, de tal manera que recuperar múltiples registros de una misma PartitionKey es muy rápido.
  • Realizar consultas que impliquen diferentes PartitionKey o buscar por otras propiedades que no formen parte de la clave penaliza el rendimiento
  • No existen las relaciones entre tablas, es decir, no podremos hacer consultas cruzadas, olvidad lo que sabéis de SQL y las reglas de normalización de tablas. Tendremos que trabajar de otra manera, seguramente repitiendo datos organizados de formas distintas en múltiples tablas.

Definición de las tablas

En C# las tablas se pueden definir directamente desde clases, podremos utilizar las mismas clases que ya teníamos, heredando de la clase TableEntity:

public class Jugador:TableEntity
{
    public Jugador()
    {
    }

    public Jugador(string pais, string nombre, string id)
    {
        PartitionKey = pais;
        RowKey = nombre;
        Id = id;
    }

    public string Pais { get { return PartitionKey; } }
    public string Nombre { get { return RowKey; } }
    public string Id { get; set; }
}

Al transformar la clase necesitamos añadir un constructor por defecto y convertimos los campos país y nombre en la PartitionKey y RowKey respectivamente.
Así podremos realizar consultas sobre todos los jugadores de un mismo país sin perder rendimiento.
El problema que nos encontraremos será encontrar el jugador por Id de conexión, algo que hacemos bastante dentro de la clase DatosPartida. Como una consulta por Id nos penalizará el rendimiento, lo que haremos será crear otra tabla para poder buscar por Id.

public class JugadorPorId : TableEntity
{
    public JugadorPorId() { 
    }

    public JugadorPorId(string pais, string nombre, string id)
    {
        PartitionKey = pais;
        RowKey = id;
        Nombre = nombre;
    }

    public string Pais { get { return PartitionKey; } }
    public string Nombre { get; set; }
    public string Id { get { return RowKey; } }
}

El caso de las partidas es más complicado, pues los jugadores vienen de otra tabla y no podemos guardar en las tablas árboles de objetos, tienen que ser objetos bastante planos. En nuestro caso vamos a modificar un poco la forma en que se guardan esas propiedades en los métodos WriteEntity y ReadEntity

public class Partida:TableEntity
{
    public const int Dimension = 3;
    public Partida()
    {
    }

    public Partida(string pais, string id)
    {
        PartitionKey = pais;
        RowKey = id;
        _tablero = new char[Dimension * Dimension];
        for (int i = 0; i < _tablero.Length; i++)
        {
            _tablero[i] = ' ';
        }
    }

    public string Pais { get { return PartitionKey; } }
    public string Id { get { return RowKey; } }
    public Jugador Jugador1 { get; set; }
    public Jugador Jugador2 { get; set; }
    public int Turno { get; set; }

    public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
    {
        var context= base.WriteEntity(operationContext);
        context.Remove("Jugador1");
        context.Add("Jugador1Nombre", EntityProperty.GeneratePropertyForString(Jugador1.Nombre));
        context.Add("Jugador1Id", EntityProperty.GeneratePropertyForString(Jugador1.Id));
        context.Remove("Jugador2");
        context.Add("Jugador2Nombre", EntityProperty.GeneratePropertyForString( Jugador2.Nombre));
        context.Add("Jugador2Id", EntityProperty.GeneratePropertyForString(Jugador2.Id));
        return context;
    }
    public override void ReadEntity(IDictionary<string, EntityProperty> properties, 
        OperationContext operationContext)
    {
        Jugador1 = new Jugador {
            PartitionKey= properties["Pais"].StringValue,
            RowKey = properties["Jugador1Nombre"].StringValue ,
            Id = properties["Jugador1Id"].StringValue
        };
        Jugador2 = new Jugador {
            PartitionKey= properties["Pais"].StringValue,
            RowKey = properties["Jugador2Nombre"].StringValue ,
            Id = properties["Jugador2Id"].StringValue
        };
        base.ReadEntity(properties, operationContext);
    }

(no pongo el resto de código pues es igual al código del capítulo anterior).

Creación de las tablas en Azure

Hemos creado las entidades por código y las tablas las vamos a crear igual. Modificaremos la clase DatosPartida que ya teníamos definida, para que al arrancar cree las tablas si es que no existen.

public class DatosPartida
{
    static CloudTable _solicitudes;
    static CloudTable _partidas;
    static CloudTable _jugadores;
    static CloudTable _jugadoresPorId;

    static DatosPartida()
    {
        // Retrieve the storage account from the connection string.
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
            CloudConfigurationManager.GetSetting("StorageConnectionString"));

        // Create the table client.
        CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

        // Create the table if it doesn't exist.
        _solicitudes = tableClient.GetTableReference("Solicitudes");
        _solicitudes.CreateIfNotExists();

        _partidas = tableClient.GetTableReference("Partidas");
        _partidas.CreateIfNotExists();

        _jugadores = tableClient.GetTableReference("Jugadores");
        _jugadores.CreateIfNotExists();

        _jugadoresPorId = tableClient.GetTableReference("JugadoresPorId");
        _jugadoresPorId.CreateIfNotExists();
    }

En el constructor estático estamos conectando al servicio de tablas mediante una cadena de conexión que he puesto en la configuración del rol. Para definirla hacemos doble click sobre el rol:
3er.12.webrole
Y en la sección Settings podremos definir nuestro valor de configuración. Por ahora utilizaremos el emulador local del storage.
3er.13.storage
A partir de este punto, modificamos los métodos de acceso que utilizábamos antes sobre listas para que accedan a las tablas. Como veréis he procurado no consultar las tablas sin una PartitionKey:

     public DatosPartida()
     {
     }

     public Jugador NuevoJugador(string pais, string nombre, string id)
     {
         var jugador = new Jugador(pais, nombre, id);
         TableOperation insertJugador = TableOperation.InsertOrReplace(jugador);
         _jugadores.Execute(insertJugador);

         TableOperation insertJugadorId = TableOperation.InsertOrReplace(new JugadorPorId(id, pais, nombre));
         _jugadoresPorId.Execute(insertJugadorId);
         return jugador;
     }

     public Jugador NuevaSolicitud(Jugador jugador)
     {
         var solicitud = new Jugador(jugador.Pais, jugador.Nombre, jugador.Id);
         TableOperation operation = TableOperation.InsertOrReplace(solicitud);
         _solicitudes.Execute(operation);
         return solicitud;
     }

     public Jugador ObtenerSolicitud(string pais, string nombre)
     {
         var op = TableOperation.Retrieve<Jugador>(pais, nombre);
         var result = _solicitudes.Execute(op);
         var solicitud = result.Result as Jugador;
         return solicitud;
     }

     public bool BorrarSolicitud(Jugador solicitud)
     {
         var op = TableOperation.Delete(solicitud);
         var result = _solicitudes.Execute(op);
         return result.HttpStatusCode == 204;
     }

     public Partida EmpezarPartida(string pais, Jugador jugador1, Jugador jugador2)
     {
         var partida = new Partida(pais, Guid.NewGuid().ToString())
         {
             Jugador1 = jugador1,
             Jugador2 = jugador2
         };
         var empiezaPartidaOp = TableOperation.InsertOrReplace(partida);
         _partidas.Execute(empiezaPartidaOp);
         return partida;
     }

     public Jugador ObtenerJugador(string pais, string id)
     {
         var op = TableOperation.Retrieve<JugadorPorId>(pais, id);
         var jugadorxid = _jugadoresPorId.Execute(op).Result as JugadorPorId;
         if (jugadorxid != null)
         {
             op = TableOperation.Retrieve<Jugador>(pais, jugadorxid.Nombre);
             return _jugadores.Execute(op).Result as Jugador;
         }
         return null;
     }

     public Partida ObtenerPartida(string pais, string id)
     {
         var op = TableOperation.Retrieve<Partida>(pais, id);
         return _partidas.Execute(op).Result as Partida;
     }

     public void GuardarMovimiento(Partida partida)
     {
         var replaceOp = TableOperation.Replace(partida);
         _partidas.Execute(replaceOp);
     }

     public IEnumerable<Jugador> ListaDisponibles(string pais)
     {
         var solicitudQuery = new TableQuery<Jugador>().Where(
            TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, pais));
         return _solicitudes.ExecuteQuery<Jugador>(solicitudQuery);
     }
 }

ServiceBus y SignalR

Si ejecutamos ahora la aplicación, parece que funciona™ pero tiene un gran fallo que podremos comprobar aumentando el número de instancias. Como vimos al principio del post, las diferentes instancias no se hablan entre sí, lo que provoca que si tenemos dos clientes de nuestra aplicación y cada uno está conectado a una instancia diferente, nuestra aplicación no funcionará, o sólo lo hará a medias.
Windows Azure tiene un mecanismo para resolver esto, el Service Bus, que nos permite crear suscripciones a la información agrupadas por “temas”. De esta manera, cuando algo cambie en una instancia podemos avisar a todas las otras.
Por suerte, SignalR implementa esta funcionalidad con el ServiceBus, así que sólo tendremos que crear una cuenta de ServiceBus y conectarla a nuestra aplicación, SignalR se encargará de gestionar los canales.

El ServiceBus no tiene emulador. En la versión anterior podíamos instalarlo en local, pero todavía no han publicado la nueva, así que para poder utilizarlo, incluso en local, tendremos que crear uno en una cuenta de Azure. El coste del ServiceBus es muy pequeño (en la fecha de publicación del artículo €0,0075 al mes por cada 10.000 mensajes) así que no nos vamos a arruinar por hacer unas pruebas.
Si tenéis alguna cuenta MSDN os entrará dentro de los recursos gratuitos que tenéis. Si no es así, podéis crear una cuenta de evaluación gratuita durante un mes: http://www.windowsazure.com/es-es/pricing/free-trial/

En nuestro portal de Azure creamos un espacio de nombres para nuestra aplicación, yo he sido muy original y lo he llamado tresenraya:
3er.20.servbus
Recordad ponerlo en una región que esté cerca de vuestros usuarios, pues es muy conveniente que todos los servicios que vamos a usar estén en la misma región, evita tráfico innecesario.
Una vez esté activo abrimos la información de la conexión y la copiamos:

3er.23.signalrsbconect
Esta información de conexión la guardaremos otra vez en las propiedades del rol:

3er.21.servbusconn

Para poder utilizar el ServiceBus necesitaremos importar con nuget el paquete de Microsoft.AspNet.SignalR.ServiceBus:
3er.22.signalrsb

Una vez instalada la librería para el ServiceBus, sólo nos queda avisar a SignalR que debe utilizarlo, así en el Application_Start de Global.asax.cs indicaremos a SignalR qué debe hacer:

protected void Application_Start(object sender, EventArgs e)
{
    // Register the default hubs route: ~/signalr
    RouteTable.Routes.MapHubs();

    var sbConnectionString = CloudConfigurationManager.GetSetting("ServiceBusConnectionString");
    GlobalHost.DependencyResolver.UseServiceBus(sbConnectionString, "TresEnRaya");
}

Resumen

En este capítulo hemos modificado la aplicación de juego para que funcionara bien dentro de un entorno cloud. El mayor trabajo ha sido cambiar el sistema de almacenamiento, pues el Hub de SignalR no lo hemos tocado y sólo hemos tenido que añadir dos líneas de código para que SignalR funcione correctamente con múltiples instancias.
En el próximo artículo crearemos la aplicación cliente en Windows 8. Como ya hemos hecho lo difícil, os prometo que la app de Windows 8 será coser y cantar.

Descarga el código del ejemplo de Codeplex

Juegos sociales online con SignalR y Windows Azure (1 de 3)

EOD technicians play tic-tac-toe with a children at a family day picnic

Hoy os propongo una mini-serie de 3 capítulos sobre SignalR y Windows Azure con un ejemplo de juego sencillo: el tres en raya, en el que podremos jugar contra otro oponente online.

La tarea presenta algunas complicaciones que resolveremos gracias a SignalR y Azure de forma muy sencilla:

  • Notificaciones: al realizar cada jugada debemos notificar al oponente. Hay muchas maneras de recibir esta notificación, dependiendo del navegador que utilicemos podemos hacer polling, que consiste en ir pidiendo novedades al servidor de forma cíclica, long polling o utilizar websockets para recibir la jugada. SignalR nos simplificará la tarea detectando automáticamente las capacidades de nuestro navegador y utilizando la técnica más adecuada en cada uno.
  • Escalabilidad: ¿qué pasa si nuestro juego tiene éxito y tenemos millones de usuarios simultáneos? Necesitaremos que el servicio escale y para eso tenemos Windows Azure, utilizaremos un Web Role para el servicio y Azure Storage Tables para almacenar la información.
  • Comunicaciones: al escalar a lo ancho nos encontraremos con otro problema, los clientes de un servidor no reciben información de los otros servidores, deberemos establecer un canal de comunicaciones entre los servidores para que reciban qué están haciendo los demás. Para ello utilizaremos el Service Bus de Azure, que nos proporciona un modelo de eventos y suscripciones http://www.asp.net/signalr/overview/performance-and-scaling/scaleout-in-signalr

Atacaremos estos puntos clave uno por uno y en este primer capítulo habrá bastante código. Montaremos todo el sistema base para el juego. Hoy empezamos creando la aplicación básica con SignalR, guardando la información en listas en memoria como en los ejemplos básicos, en el siguiente capítulo le añadiremos Azure y en el último añadiremos otro cliente al sistema, además del cliente web que hacemos hoy.

Antes de empezar os dejo un par de “disclaimers”:

Nota para puristas: voy a hacer bastantes simplificaciones para que este artículo sea didáctico, aunque me duela, no voy a utilizar MVVM, MVC, TDD ni nada que me aparte de lo que pretendo mostrar hoy, el uso de SignalR en Azure.

Nota para pragmáticos: no os esperéis poder hacer copy/paste del código para vuestra aplicación en producción. Aparte de lo comentado en la advertencia anterior, el ejemplo no será completo, voy a dejar algunas cosas como ejercicios para que hagáis vosotros 🙂

Y ahora que ya no tengo ninguna responsabilidad puedo empezar a hackear tranquilo.

¿Qué necesito?

Para poder compilar y probar el código vas a necesitar:

SignalR

Para crear cualquier juego multijugador online en tiempo real tenemos que establecer canales de comunicación entre todas las partes implicadas. Normalmente no es algo trivial, más si añadimos diferentes plataformas de cliente, tales como web, aplicaciones móviles y de tableta. Para que funcione bien tendremos que gestionar los posibles problemas de conexión, establecer puntos de conexión entre clientes diferentes, crear un canal de difusión para enviar notificaciones a todos los clientes, confirmar que los mensajes llegan, crear librerías de cliente que mantengan la conexión abierta con el servidor, autorizar e identificar a los usuarios y un largo etcétera de funcionalidades.

Cuando se trata de un cliente Web, añadimos otra vuelta de tuerca, pues debemos tener en cuenta las capacidades de cada navegador para decidir en el momento qué tecnología de comunicaciones funcionará mejor.

SignalR es una librería para ASP.NET que nos liberará de gestionar todos estos problemas y nos permitirá concentrarnos en la tarea que realmente queremos hacer. Nos proporciona una librería en servidor para gestionar todas las conexiones y suscripciones, además de generar dinámicamente un script que nos permitirá realizar la conexión desde el cliente.

Un tres en raya social

Partimos de una aplicación sencilla como el tres en raya para no complicar demasiado el ejemplo, algo intermedio entre  el ejemplo básico de chat y el juego online ShootR.

Empezaremos con una aplicación ASP.NET vacía:

tictactoe_01_newproject

Una vez tengamos nuestra solución, necesitaremos un archivo Global.asax y una página HTML donde mostrar nuestro tablero:

3er.01a.Globalasax

En el mismo menú seleccionamos HTML Page y la llamamos default:

3er.01b.defaulthtmlDentro del body de la página colocamos un panel de tres en raya:

<div id="partida">
    <button id="empezar">Empezar nueva partida</button>
    <style>
        #tablero {
            height: 160px;
            width: 160px;
            padding: 5px;
        }

        .row {
            height: 50px;
            width: 150px;
        }

        .col {
            height: 48px;
            width: 48px;
            border: 1px solid black;
            float: left;
            text-align: center;
            line-height: 48px;
        }

        .row0, .row2 {
            background-color: rgba(0,0,0,0.2);
        }

        .col1 {
            background-color: rgba(0,0,0,0.2);
        }
    </style>

    <div id="tablero">
        <div class="row row0">
            <div class="col col0"></div>
            <div class="col col1"></div>
            <div class="col col2"></div>
        </div>
        <div class="row row1">
            <div class="col col0"></div>
            <div class="col col1"></div>
            <div class="col col2"></div>
        </div>
        <div class="row row2">
            <div class="col col0"></div>
            <div class="col col1"></div>
            <div class="col col2"></div>
        </div>
    </div>

    <div>
        <h2>Solicitud de partidas</h2>
        <div id="partidas">
            <div>Cargando...</div>
        </div>
    </div>
</div>
<div>
    <h2>Mensajes</h2>
    <div id="mensajes"></div>
</div>

En cuanto al código, empezaremos con la parte de servidor. Vamos a utilizar SignalR, necesitaremos añadir las referencias a las librerías en nuestro proyecto. Por suerte tenemos la herramienta Nuget que nos permitirá descargar e instalar en nuestro proyecto todo lo necesario con un click:
3er.02.alt.Nuget
Buscamos en la sección Online por SignalR:
3er.02.signalr
Nuget nos instalará todas las dependencias que necesita la librería y acto seguido nosotros cambiaremos el evento Application_Start en el Global.asax.cs, tal como nos indica el propio SignalR al instalarse:

protected void Application_Start(object sender, EventArgs e)
{
    // Register the default hubs route: ~/signalr
    RouteTable.Routes.MapHubs();
}

Esta pequeña línea realiza la magia que nos permitirá comunicarnos con los clientes desde el servidor. En nuestro proyecto ya podemos crear un Hub, el punto de conexión entre los clientes y el servidor, al que llamaremos PartidaHub:
3er.03.signalrhub

La plantilla nos creará a mínima expresión de un Hub:

public class PartidaHub : Hub
{
    public void Hello()
    {
        Clients.All.hello();
    }
}

Este código de servidor que se ha generado automáticamente, está llamando a una función definida en todos los clientes conectados a este Hub llamada “hello”; SignalR se encargará de que eso ocurra.
En el Hub vamos a crear métodos que podrán ser llamados por el cliente y el mismo hub, a su vez, podrá realizar llamadas a todos los clientes, al emisor del mensaje o a receptores concretos, mediante los siguientes métodos:

//como el anuncio aquel de refrescos...
//para todos
Clients.All.hello();
//para el que llama
Clients.Caller.hello();
//para todos los demás
Clients.Others.hello();
//para uno en concreto
Clients.Client(id).hello();

Si vamos a mirar la definición de estas propiedades y métodos veremos que son de tipo dynamic, lo que nos permite escribir nombres de funciones que no tenemos definidas en ningún sitio de nuestro código C#.

Datos de partida

Antes de ponernos con el Hub creamos unas cuantas estructuras que necesitamos para jugar: los jugadores, las partidas y las solicitudes de partida, para que un jugador pueda empezar una partida y así otro pueda apuntarse.
Creamos una carpeta Data y empezamos a crear unas cuantas clases, la primera una clase para almacenar la partida y la lógica de juego:

public class Partida
{
    public const int Dimension = 3;

    public Partida(string pais, string id)
    {
        Pais = pais;
        Id = id;
        _tablero = new char[Dimension * Dimension];
        for (int i = 0; i < _tablero.Length; i++)
        {
            _tablero[i] = ' ';
        }
    }

    public string Pais { get; set; }
    public string Id { get; set; }
    public Jugador Jugador1 { get; set; }
    public Jugador Jugador2 { get; set; }
    public int Turno { get; set; }

    char[] _tablero;
    public string Tablero
    {
        get
        {
            return new string(_tablero);
        }
        set
        {
            _tablero = value.ToCharArray();
        }
    }

    public bool Marcar(int x, int y, int jugador)
    {
       if (Estado == EstadoPartida.EnJuego)
       {
           var posicion = Dimension * x + y;
           if (jugador == Turno)
           {
               if (_tablero[posicion] == ' ')
               {
                   var valor = Turno == 0 ? 'O' : 'X';
                   _tablero[Dimension * x + y] = valor;
                   Turno = Turno == 0 ? 1 : 0;
                   return ComprobarFinal();
               }
               else
               {
                   throw new InvalidOperationException("La casilla está ocupada");
               }
           }
           else
           {
               throw new InvalidOperationException("No es tu turno");
           }
       }
       else
       {
           throw new InvalidCastException("El juego ha acabado");
       }
   }

   public char Valor(int x, int y)
   {
       return _tablero[Dimension * x + y];
   }

   bool ComprobarFinal()
   {
       int[] jugadas = new int[Partida.Dimension * 2 + 2];

       for (int i = 0; i < Partida.Dimension; i++)
       {
           for (int j = 0; j < Partida.Dimension; j++)
           {
               var v = Valor(i, j);
               int sum = 0;
               if (v == 'X')
               {
                   sum = -1;
               }
               else if (v == 'O')
               {
                   sum = 1;
               }
               jugadas[i] += sum;
               jugadas[Partida.Dimension + j] += sum;
               if (i == j)
               {
                   jugadas[Partida.Dimension * 2] += sum;
               }
               if (i + j == Partida.Dimension - 1)
               {
                   jugadas[Partida.Dimension * 2 + 1] += sum;
               }
           }
       }
       if (jugadas.Count((x) => x == 3) > 0)
       {
           Estado = EstadoPartida.Gana1;
       }
       else if (jugadas.Count((x) => x == -3) > 0)
       {
           Estado = EstadoPartida.Gana2;
       }
       else if (!Tablero.Contains(' '))
       {
           Estado = EstadoPartida.Empate;
       }

       return Estado != EstadoPartida.EnJuego;
   }
}

También necesitaremos una clase Jugador para identificarlos:

public class Jugador
{
    public Jugador(string pais, string nombre, string id)
    {
        Nombre = nombre;
        Pais = pais;
        Id = id;
    }

    public string Pais { get; set; }
    public string Nombre { get; set; }
    public string Id { get; set; }
}

Y una clase DatosPartida que nos permita ir añadiendo jugadores, solicitudes y partidas de forma ordenada. Esta clase nos vendrá bien a la hora de transformar nuestra aplicación a Azure:

public class DatosPartida
{
    List<Jugador> _jugadores = new List<Jugador>();
    List<Partida> _partidas = new List<Partida>();
    List<Jugador> _solicitudes = new List<Jugador>();

    public DatosPartida()
    {
    }

    public Jugador NuevoJugador(string pais, string nombre, string id)
    {
        var jugador=_jugadores.FirstOrDefault(j => j.Pais == pais && j.Nombre == nombre);
        if (jugador == null)
        {

            jugador = new Jugador(pais, nombre, id);
            _jugadores.Add(jugador);
        }
        else
        {
            //actualiza el id del jugador, imaginaremos, por ahora, que si se llaman igual es el mismo...
            jugador.Id = id;
        }
        return jugador;
    }

    public Jugador NuevaSolicitud(Jugador jugador)
    {
        _solicitudes.Add(jugador);
        return jugador;
    }

    public Jugador ObtenerSolicitud(string pais, string nombre)
    {
        return _solicitudes.FirstOrDefault(s => s.Pais == pais && s.Nombre == nombre);
    }

    public bool BorrarSolicitud(Jugador solicitud)
    {
        return _solicitudes.Remove(solicitud);
    }

    public Partida EmpezarPartida(string pais, Jugador jugador1, Jugador jugador2)
    {
        var partida = new Partida(pais, Guid.NewGuid().ToString())
        {
            Jugador1 = jugador1,
            Jugador2 = jugador2
        };
        _partidas.Add(partida);
        return partida;
    }

    public Jugador ObtenerJugador(string pais, string id)
    {
        return _jugadores.FirstOrDefault(jugador => jugador.Id == id);
    }

    public Partida ObtenerPartida(string pais, string id)
    {
        return _partidas.FirstOrDefault(p=>p.Pais==pais && p.Id==id);
    }

    public void GuardarMovimiento(Partida partida)
    {
        var old=ObtenerPartida(partida.Pais, partida.Id);
        _partidas.Remove(old);
        _partidas.Add(partida);
    }

    public IEnumerable<Jugador> ListaDisponibles(string pais)
    {
        return _solicitudes.Where((s)=>s.Pais==pais);
    }
}

Y finalmente escribiremos el Hub, donde cada vez que queramos notificar algo a un cliente o a muchos utilizaremos los métodos dinámicos de Clients.All , Clients.Caller, etc.:

public class PartidaHub : Hub
{
    private readonly static Lazy<DatosPartida> _datos = new Lazy<DatosPartida>(true);
    public DatosPartida Datos { get { return _datos.Value; } }

    public Jugador NuevaPartida(string pais, string nombre)
    {
        var jugador = Datos.NuevoJugador(pais, nombre, Context.ConnectionId);
        var solicitud=Datos.NuevaSolicitud(jugador);
        Clients.All.Nueva(solicitud);  //avisamos a todos que una nueva partida ha sido solicitada
        return jugador;
    }

    public IEnumerable<Jugador> PartidasDisponibles(string pais)
    {
        return Datos.ListaDisponibles(pais);
    }

    public bool Jugar(string pais, string jugador1, string jugador2)
    {
        var solicitud = Datos.ObtenerSolicitud(pais, jugador1);
        if (solicitud != null)
        {
            var jugadorB=Datos.ObtenerJugador(pais, Context.ConnectionId);
            if (jugadorB == null)
                jugadorB = Datos.NuevoJugador(pais, jugador2, Context.ConnectionId);
            Partida partida = Datos.EmpezarPartida(pais, solicitud, jugadorB);

            //eliminamos las solicitudes pendientes de los jugadores
            Datos.BorrarSolicitud(solicitud);
            var solicitud2 = Datos.ObtenerSolicitud(jugadorB.Pais, jugadorB.Nombre);
            if (solicitud2 != null)
            {
                Datos.BorrarSolicitud(solicitud2);
            }

            //notificacmos solicitudes eliminadas
            Clients.All.PartidaEliminada(solicitud);
            if (solicitud2 != null)
            {
                Clients.All.PartidaEliminada(solicitud2);
            }
            //notificamos al llamante sobre el comienzo de la partida
            Clients.Caller.Jugando(partida);
            //notificamos al jugador que solicitó una partida sobre el comienzo de la misma
            Clients.Client(solicitud.Id).Jugando(partida);
            return true;
        }
        else
        {
            error("Ya no existe la partida");
        }
        return false;
    }

    public void MarcaCasilla(string pais, string nombre, int x, int y)
    {
        var partida = Datos.ObtenerPartida(pais, nombre);

        if (partida != null)
        {
            int turno = 0;
            if (Context.ConnectionId == partida.Jugador2.Id)
                turno = 1;
            int turnoAnterior = partida.Turno;

            try
            {
                bool acabada = partida.Marcar(x, y, turno);
                Datos.GuardarMovimiento(partida);
                Clients.Client(partida.Jugador1.Id).Jugada(partida);
                Clients.Client(partida.Jugador2.Id).Jugada(partida);

                if (acabada)
                {
                    switch (partida.Estado)
                    {
                        case EstadoPartida.Empate:
                            Clients.Client(partida.Jugador1.Id).FinJuego("Esta vez hay empate");
                            Clients.Client(partida.Jugador2.Id).FinJuego("Esta vez hay empate");
                            break;
                        case EstadoPartida.Gana1:
                            Clients.Client(partida.Jugador1.Id).FinJuego("Ganaste la partida!!!");
                            Clients.Client(partida.Jugador2.Id).FinJuego("Esta vez te ganó " + partida.Jugador1.Nombre);
                            break;
                        case EstadoPartida.Gana2:
                            Clients.Client(partida.Jugador2.Id).FinJuego("Ganaste la partida!!!");
                            Clients.Client(partida.Jugador1.Id).FinJuego("Esta vez te ganó " + partida.Jugador2.Nombre);
                            break;
                    }
                }
            }
            catch (InvalidOperationException ex)
            {
                mensaje(ex.Message);
            }
        }
        else
        {
            error("no existe la partida");
        }
    }

    private void error(string error)
    {
        mensaje("Error: " + error);
    }

    private void mensaje(string msg)
    {
        Clients.Caller.Mensaje(msg);
    }
}

Código en cliente

En el lado del cliente vamos a definir unas funciones para comunicarnos con el servidor. Lo primero que necesitamos es obtener el Hub que hemos definido antes. SignalR crea para nosotros una librería JavaScript que tiene todo lo necesario para comunicarnos con este.
Creamos en la carpeta Scripts un script “tresenraya.js” y añadimos al final de la página html las referencias a los siguientes scripts, antes del cierre de la etiqueta body:

<script src="Scripts/jquery-2.0.1.min.js"></script>
<script src="Scripts/jquery.signalR-1.1.2.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
<script src="Scripts/tresenraya.js"></script>

El script /signalr/hubs es el que genera SignalR automáticamente con el código para acceder a nuestros hubs.
Una vez hemos añadido los scripts, podemos empezar a escribir nuestro tresenraya.js. En primer lugar necesitamos conectar al hub e inicializar, en nuestro versión sencilla pediremos el nombre al usuario nada más empezar, lo que vendría a ser el login de los pobres (y confiados). Para el código js usamos jQuery1.6.4 que es el que nos ha instalado SignalR:

$(function () {
    //obtenemos el hub
    var servicioPartidas = $.connection.partidaHub;
    //iniciamos la conexión y una vez iniciada (método done) llamamos a nuestra función de inicialización
    $.connection.hub.start().done(init);

    var pais = "Mallorca";  //para no pedir tantos datos al principio vamos a suponer 
                            //que estáis todos de vacaciones en mi isla ^^

    function init() {
        nombre = prompt("Escribe tu nombre");
        //llamamos a una función de servidor que nos devuelve una lista de partidas
        //que se realizan en Mallorca ^^
        servicioPartidas.server.partidasDisponibles(pais).done(function (list) {
            $partidas.empty();
            $.each(list, function () {
                crearEntrada(this);
            });

        });
        //botón nueva partida
        $btnEmpezar.bind("click", null, function (e) {
            servicioPartidas.server.nuevaPartida(pais, nombre);
            return false;
        });
        //inicializamos los recuadros del 3 en raya
        for (var row = 0; row < 3; row++) {
            for (var col = 0; col < 3; col++) {
                var $celda = $(".row" + row + " .col" + col);
                $celda.click(function () {
                    var x = row;
                    var y = col;
                    return function () {
                        if (partidaActual != null) {
                            servicioPartidas.server.marcaCasilla(partidaActual.Pais, partidaActual.Id, x, y);
                        }
                    }
                }());
            }
        }
    }

Fijaos que estamos llamando a los métodos que definimos en nuestro Hub: nuevaPartida, marcarCasilla, etc., y usamos los mismos parámetros que hemos definido en el código C#.

Para el código anterior necesitamos unas cuantas funciones más que nos permiten mostrar las solicitudes de partida que van llegando:

$btnEmpezar = $("#empezar");
$partidas = $("#partidas");
$mensajes = $("#mensajes");
var partidaActual;
var nombre;
var partidaListItem = "<div id='{Pais}_{Nombre}item'>Pais: {Pais} Usuario:{Nombre} <button id='{Pais}_{Nombre}'>Jugar</button></div>";

// A simple templating method for replacing placeholders enclosed in curly braces.
if (!String.prototype.supplant) {
    String.prototype.supplant = function (o) {
        return this.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' ? r : a;
            }
        );
    };
}

function crearId(entity) {
    return entity.Pais + "_" + entity.Nombre;
}

function crearEntrada(solicitud) {
    if (solicitud.Nombre != nombre) {
        $partidas.append(partidaListItem.supplant(solicitud));
        $("#" + crearId(solicitud)).click(function (e) {
            var valores = e.currentTarget.id.split("_");
            servicioPartidas.server.jugar(valores[0], valores[1],nombre);
            return false;
        });
    }
}

Recibir notificaciones del servidor

Hasta ahora hemos escrito código que envía información, pero también queremos recibir el push que realiza el servidor hacia los clientes. Para esto tenemos que definir unas funciones con los mismos nombres y parámetros que utilizamos en el lado del servidor.
Si recordáis el código anterior, al crear una nueva solicitud de partida llamábamos al método Clients.All.Nueva(solicitud);. Este es el método que tendremos que escribir en el cliente, por ejemplo, con el siguiente código recibimos el mensaje de partidas nuevas:

servicioPartidas.client.nueva = function (solicitud) {
    if (solicitud.Nombre == nombre && solicitud.Pais == pais) {
        //si es tu partida deshabilita el botón de empezar.. ya estás jugando
        $btnEmpezar.text("Esperando a que llegue un jugador");
        $btnEmpezar.prop("disabled", true);
        $partidas.prop("disabled", true);
    }
    else {
        //si no es tu partida saca la lista
        crearEntrada(solicitud);
    }

Aquí cuando se nos notifica el principio y final del juego:

servicioPartidas.client.jugando = function (partida) {
    partidaActual = partida;
    $btnEmpezar.text("Jugando partida " + partida.Jugador1.Nombre + " contra " + partida.Jugador2.Nombre);
    $btnEmpezar.prop("disabled", true);
    $partidas.prop("disabled", true);
    pintarTablero(partida.Tablero);
};

function pintarTablero(tablero) {
    for (var row = 0; row < 3; row++) {
        for (var col = 0; col < 3; col++) {
            var $celda = $(".row" + row + " .col" + col);
            $celda.text(tablero[3 * row + col]);
        }
    }
}

servicioPartidas.client.finJuego = function (mensaje) {
    $mensajes.append(mensaje + "<br/>");
    $btnEmpezar.text("Empezar nuevo juego");
    $btnEmpezar.prop("disabled", false);
    $partidas.prop("disabled", false);
}

Cada vez que hay una jugada desde el servidor se nos manda el tablero y lo volvemos a representar:

servicioPartidas.client.jugada = function (partida) {
    //cuando llega una jugada de nuestra partida en curso
    //pintamos el tablero
    pintarTablero(partida.Tablero);
}

Y dos funciones más que también necesitamos:

//los mensajes también irán apareciendo
servicioPartidas.client.mensaje = function (msg) {
    $mensajes.append(msg + "<br/>");
}

servicioPartidas.client.partidaEliminada = function (solicitud) {
    $("#" + crearId(solicitud) + "item").remove();
}

Tras escribir (o cortapegar) todo este código ya os debería funcionar la primera versión del tres en raya online. Mañana transformaremos el código para que funcione en un WebRole de Windows Azure.
3er.01.pacowins
Espero vuestros comentarios!!! Id pensando cómo haríais que esta aplicación funcione bien en Azure.

Si habéis llegado hasta aquí os merecéis este vídeo viejuno que os explicará por qué he elegido el tres en raya:

Binding a imágenes de carpetas especiales en Windows 8

Descarga el código de este artículo en Codeplex

Hoy para cambiar un poco haré un ejemplo en XAML/C#, pero seguimos con aplicaciones de la tienda Windows.
Os explicaré un truco que se puede realizar de dos formas: la primera, la forma automática, utilizando una clase del sistema que ya nos hace casi todo el trabajo y la segunda, una forma manual de realizarlo, que dejaremos para más adelante.

Las carpetas especiales

En las aplicaciones de la Tienda Windows podemos acceder directamente a algunas carpetas especiales del sistema siempre que declaremos en el manifiesto que queremos acceder y el usuario lo sepa:
pictureslibrary
Una vez hemos establecido el requisito en el manifiesto, ya podemos utilizar la clase Windows.Storage.KnownFolders para listar los elementos de la carpeta de imágenes:

var queryOptions = new Windows.Storage.Search.QueryOptions();
queryOptions.FolderDepth = FolderDepth.Deep;
var query = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);

Nota: si intentáis esto en otra carpeta, como por ejemplo “Mis Documentos”, no funcionará, pues también hay que declarar que tipos de archivo vamos a abrir y sólo podremos abrir esos.

Listar todos los archivos dentro de la carpeta

Para obtener una lista de todos los archivos podemos llamar al método GetFilesAsync() y luego podremos trabajar sobre la misma. Si vamos a representar esa lista en el interfaz de usuario, WinRT nos ofrece una forma mucho mejor: podemos obtener una lista virtualizada, esto es, una lista con el mismo número de elementos que contiene la carpeta y las características de esos elementos se van obteniendo de forma asíncrona. De esta forma obtendremos los primeros resultados mucho más rápido y veremos en nuestra colección en pantalla el tamaño completo de la lista, aunque todavía no se hayan obtenido todos los resultados.

public class DataSource:INotifyPropertyChanged
{
    object _items;
    public object Items
    {
        get { return _items; }
        private set
        {
            _items = value;
            onPropertyChanged("Items");
        }
    }

    public DataSource()
    {
        try
        {
            var queryOptions = new Windows.Storage.Search.QueryOptions();
            queryOptions.FolderDepth = FolderDepth.Deep;
            var query = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);
            var fileInformationFactory = new FileInformationFactory(query,
                Windows.Storage.FileProperties.ThumbnailMode.PicturesView,
                150, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, true);
            Items = fileInformationFactory.GetVirtualizedFilesVector();
        }
        catch (Exception ex)
        {
            Items = new object[] {new{ Name = ex.Message} };
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void onPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Si observamos el código, veremos que la clase FileInformationFactory nos permite generar también una vista previa de cada uno de los archivos. El método no es asíncrono, esto quiere decir que nos devolverá muy rápidamente un resultado: la lista virtualizada llena de elementos vacíos, que luego se irán rellenando en segundo plano.

Ahora sólo nos hace falta crear un contenedor GridView para mostrar los archivos:

<Page
    x:Class="ImageBinding.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ImageBinding"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:localData="using:ImageBinding.DataModel">
    <Page.Resources>
        <localData:DataSource x:Key="mainDataSource"></localData:DataSource>
        <CollectionViewSource Source="{Binding Path=Items, Source={StaticResource mainDataSource}}"
                              x:Key="mainView"></CollectionViewSource>
        <DataTemplate x:Key="DataTemplate">
            <Grid Width="150">
                <TextBlock Text="{Binding Name}" TextTrimming="WordEllipsis" ></TextBlock>
            </Grid>
        </DataTemplate>
    </Page.Resources>
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" 
        DataContext="{StaticResource mainDataSource}">
        <GridView
            Margin="0 90 0 0" ItemsSource="{Binding Source={StaticResource mainView }}" 
                  ItemTemplate="{StaticResource DataTemplate}" SelectionMode="None">
        </GridView>
    </Grid>
</Page>

Carga de imágenes

Una vez tenemos la lista de archivos vamos a mostrar las imágenes que os prometí al principio del artículo. La colección que nos proporciona FileInformationFactory.GetVirtualizedFilesVector() contiene elementos que cumplen con el interfaz IStorageItemInformation. Estos elementos tienen una propiedad Thumbnail a la que podremos enlazarnos para visualizar la imagen, pero no podremos hacerlo directamente pues el tipo StorageItemThumbnail en realidad es un tipo Stream y no una imagen, así que necesitaremos un Converter para poder utilizar la imagen dentro del interfaz de usuario:

public class StreamToBitmapConverter:IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value != null)
        {
            var stream = (IRandomAccessStream)value;
            var img = new BitmapImage();
            img.SetSource(stream);
            return img;
        }
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

A través del Converter ya podremos hacer el enlace correctamente:

<Page.Resources>
    <localData:DataSource x:Key="mainDataSource"></localData:DataSource>
    <CollectionViewSource Source="{Binding Path=Items, Source={StaticResource mainDataSource}}"
                          x:Key="mainView"></CollectionViewSource>
    <localConverters:StreamToBitmapConverter x:Key="bitmapConverter"/>
    <DataTemplate x:Key="DataTemplate">
        <Grid Width="150">
            <Grid.RowDefinitions>
                <RowDefinition Height="70"/>
                <RowDefinition Height="32"/>    
            </Grid.RowDefinitions>
            <Image Grid.RowSpan="2" Source="{Binding Thumbnail, 
                Converter={StaticResource bitmapConverter}}"></Image>
            <Border Grid.Row="1" Background="#80000000" Padding="5">
                <TextBlock Text="{Binding Name}" VerticalAlignment="Center" 
                           TextTrimming="WordEllipsis" />
            </Border>
        </Grid>
    </DataTemplate>
</Page.Resources>

Y aquí tenemos el resultado:
picturesCollection
En el próximo artículo os contaré cómo hacer lo mismo pero programando nosotros la carga asíncrona de imágenes bajo demanda.

Descarga el código de este artículo en Codeplex