Creando un control de usuario reutilizable : El mapa de México en XAML en acción - Parte III
Esta es la continuación del artículo "Creando un control de usuario reutilizable : El mapa de México en XAML en acción Parte II”
El portal que estamos desarrollando con Silverlight 2 tiene un control de usuario que representa el mapa de México. El control es sensible a los clics del mouse y levanta un evento cuando un estado es seleccionado. No obstante, la solución en este momento no es muy funcional ya que lo único que hace es mostrar el nombre del estado en un TextBlock. En este artículo veremos cómo extender la funcionalidad del Portal agregando una funcionalidad sencilla pero poderosa: obtenendremos una serie de fotografías relacionadas con el estado seleccionado usando los servicios REST públicos de Flickr.
REST
REST es el acrónimo de REpresentational State Transfer, el cual es una serie de lineamientos y prácticas para la arquitectura de servicios usando las propias bondades de HTTP y sus verbos (PUT, GET, DELETE, POST) los cuales pueden derivar fácilmente en acciones CRUD (Create, Read, Update, Delete) y por ende en acciones para obtener o modificar datos.
Comunmente en el ámbito de Servicios Web tradicionales (ASMX o WCF) tenemos métodos marcados especialmente para responder a peticiones y regresar algún conjunto de datos o realizar alguna operación. No obstante, para cada operación o acción requerimos crear un endpoint diferente cada vez (no necesariamente, pero muy comunmente es así), es decir, vamos creando métodos y más métodos para satisfacer las necesidades operativas de nuestro servicio. En REST no necesariamente es lo mismo.
Un ejemplo de un servicio REST puede ser:
/productos">http://<sitio>/productos
para regresar una lista completa de productos, o
/productos/1001">http://<sitio>/productos/1001
para regresar únicamente el producto con identificador 1001.
API de Flickr
Vamos a utilizar los servicios REST que expone Flickr de manera gratuita y pública para implementar la funcionalidad de obtención de imágenes en nuestro Portal que estamos desarrollando. La documentación completa de estos servicios la pueden consultar aquí. Es importante mencionar que para la correcta ejecución de los servicios y obtención de datos necesitan un identificador único, el cual es obtenido de manera sencilla inscribiéndote al sitio de Flickr. Ese identificador está relacionado con tu cuenta así que mucho cuidado en dónde lo usen o muestren.
El API de Flickr indica la manera en la que puedes consultar sus servicios y cómo serán regresados esos datos. Para nuestra aplicación utilizaremos el método flickr.photos.search para buscar todas aquellas fotos que están etiquetadas con el nombre del estado que hemos seleccionado en el mapa. Cabe mencionar que los servicios de Flickr tienen muchas más funcionalidades para usarlas en nuestras aplicaciones, como etiquetado de fotos, consulta de grupos, contactos, modificación de los metadatos de las fotos, etc. Para más información, consulta la documentación en línea.
URL del servicio
La dirección, entonces, del servicio que estaremos usando es:
URL de las fotos
Cada foto en Flickr tiene un URL el cual está constituído de la siguiente manera:
http://farm{FARM}.static.flickr.com/{SERVIDOR}/{ID}_{SECRETO}.jpg
Tal es el caso de la siguiente imagen, que tiene el URL https://farm4.static.flickr.com/3159/3046189596_1e8821aaaf_o.jpg
Implementación
Estados
Primero lo que vamos a hacer es corregir el hecho de que algunos estados de México tienen más de una palabra, y que los Canvas que representan cada estado de la República tienen un nombre corto, por ejemplo Baja California Sur es BCSur o San Luis Potosí es SanLuisP. Dudo mucho que las personas que suban imágenes a Flickr etiqueten sus fotos de esa manera. Por lo tanto, vamos a agregar la siguiente clase a nuestro proyecto de Silverlight la cual es una clase que hereda de la clase genérica Dictionary<T, T>, y en nuestro caso la implementaremos como Dictionary<string, string>.
public class Estados : Dictionary<string, string>
{
public Estados()
{
Add("Aguascalientes", "Aguascalientes");
Add("BCNorte", "\"Baja California\"");
Add("BCSur", "\"Baja California Sur\"");
Add("Campeche", "Campeche");
Add("Coahuila", "Coahuila");
Add("Colima", "Colima");
Add("Chiapas", "Chiapas");
Add("Chihuahua", "Chihuahua");
Add("DistritoFederal", "\"Distrito Federal\"");
Add("Durango", "Durango");
Add("Guanajuato", "Guanajuato");
Add("Guerrero", "Guerrero");
Add("Hidalgo", "Hidalgo");
Add("Jalisco", "Jalisco");
Add("EdoMexico", "\"Estado de México\"");
Add("Michoacan", "Michoacán");
Add("Morelos", "Morelos");
Add("Nayarit", "Nayarit");
Add("NuevoLeon", "\"Nuevo León\"");
Add("Oaxaca", "Oaxaca");
Add("Puebla", "Puebla");
Add("Queretaro", "Querétaro");
Add("QuintanaRoo", "\"Quintana Roo\"");
Add("SanLuisP", "\"San Luis Potosí\"");
Add("Sinaloa", "Sinaloa");
Add("Sonora", "Sonora");
Add("Tabasco", "Tabasco");
Add("Tamaulipas", "Tamaulipas");
Add("Tlaxcala", "Tlaxcala");
Add("Veracruz", "Veracruz");
Add("Yucatan", "Yucatán");
Add("Zacatecas", "Zacatecas");
}
}
Posteriormente, es buena idea tener un control que muestre las fotos relacionadas al estado seleccionado y eso lo vamos a lograr con un control de tipo ListBox en Page.xaml. El control lo vamos a situar en la segunda columna y en la segunda fila, ocupando dos filas de espacio.
<ListBox x:Name="listaFotos" BorderBrush="Transparent" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Url}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Es importante hacer notar que estamos modificando la plantilla del ListBox para cada uno de sus elementos, y estamos usando el elemento <DataTemplate> en el cual definimos lo que queremos que se vea para cada uno de los elementos. En nuestro caso implementamos un elemento <Image> y hacemos atado de datos a la propiedad Url explicada anteriormente en este artículo. El atado de datos (data binding en inglés) es una característica sumamente importante y poderosa de Silverlight la cual tendrá su propia saga de artículos más adelante en este blog.
Después lo que vamos a hacer es agregar a nuestro proyecto de Silverlight una clase que represente una foto o imagen de Flickr. El siguiente fragmento de código muestra la clase Foto con sus respectivas propiedades (Farm, Server, ID, Secret) y una quinta propiedad llamada Url la cual regresará la cadena compuesta, es decir, el Url tal y como lo espera Flickr para poder mostrar fotos.
public class Foto
{
public string ID { get; set; }
public string Farm { get; set; }
public string Server { get; set; }
public string Secret { get; set; }
public string Url
{
get
{
return string.Format("http://farm{0}.static.flickr.com/{1}/{2}_{3}_m.jpg",
Farm, Server, ID, Secret);
}
}
}
Además, vamos a modificar un poco nuestro control Mexico.xaml, agregando un segundo evento llamado EstadoSeleccionadoMouse el cual se disparará cuando simplemente pasemos el cursor del mouse sobre alguno de los estados. Esto lo queremos hacer así por el hecho de determinar con anticipación el nombre del estado antes de hacer click, y evitar peticiones innecesarias al servidor de Flickr y de esta manera ahorrarnos ancho de banda. El evento EstadoSeleccionadoMouse estará también basado en el mismo delegado MexicoEventHandler creado y explicado en los artículos anteriores.
public event MexicoEventHandler EstadoSeleccionadoMouse;
void estado_MouseEnter(object sender, MouseEventArgs e)
{
((Path)((Canvas)sender).Children[0]).Fill = new SolidColorBrush(Colors.Red);
((Path)((Canvas)sender).Children[0]).Stroke = new SolidColorBrush(Colors.Yellow);
if (EstadoSeleccionadoMouse != null)
{
EstadoSeleccionadoMouse(this,
new MexicoEventArgs() { Estado = ((Canvas)sender).Name });
}
}
Ahora bien, cuando se levante el evento EstadoSeleccionado de nuestro control (cuando el usuario haga clic en algún estado) vamos a hacer una petición al servicio de Flickr para obtener todas las fotos relacionadas con el estado seleccionado (aquellas que estén etiquetadas con ese nombre).
Esto lo vamos a hacer utilizando un objeto de tipo WebClient, el cual encapsula la funcionalidad necesaria para hacer llamadas asíncronas al endpoint que definas en el método que estés utilizando: DownloadStringAsync o OpenReadAsync. En nuestro caso usaremos DownloadStringAsync ya que la respuesta del servicio de Flickr es texto plano expresado ya sea en XML o en JSON.
mapaMexico.EstadoSeleccionado += (o, args) =>
{
string estadoSeleccionado = estados[args.Estado];
string flickrApi = string.Format("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key={AQUI VA TU API KEY}&tags={0}", estadoSeleccionado);
WebClient rest = new WebClient();
rest.DownloadStringCompleted += (sender, e) =>
{
XDocument doc = XDocument.Parse(e.Result);
var fotos = from results in doc.Descendants("photo")
select new Foto
{
ID = results.Attribute("id").Value.ToString(),
Farm = results.Attribute("farm").Value.ToString(),
Server = results.Attribute("server").Value.ToString(),
Secret = results.Attribute("secret").Value.ToString()
};
listaFotos.ItemsSource = fotos;
};
rest.DownloadStringAsync(new Uri(flickrApi));
};
Asimismo, es importante notar que estamos usando una consulta de LINQ To XML para obtener todas las fotos que regresa el servicio en forma de elementos Xml y por cada una de ellas regresaremos una instancia de Foto, la cual tiene implementada la propiedad Url que regresa ya armada el URL correcto y es la propiedad a la que se ata o liga el elemento <Image> dentro del <DataTemplate>… todo concuerda!
Todo listo
Ejecutemos la aplicación y veamos que inicialmente tiene la misma apariencia que la aplicación del anterior artículo, no obstante, si hacemos clic en algún estado, en la parte derecha se muestran todas las fotos relacionadas con él.
Incluso, podemos corroborar que efectivamente sean las fotos correctas desde el mismo sitio de Flickr, haciendo una búsqueda por la palabra completa “Nuevo León” (como se muestra en el ejemplo). Ojo: hay que incluir las comillas para determinar que es una palabra compuesta y no dos palabras por separado (Nuevo y León).
El código fuente de este artículo lo pueden descargar de la sección “Contenido” de La Liga Silverlight o haciendo clic directamente aquí.