viernes, 2 de julio de 2010

Windows Phone 7: Nuestra primera aplicación.

En algunos meses Microsoft nos traerá la nueva versión de su sistema operativo para móviles, Windows Phone 7 y con el muchos cambios y novedades, polémicas, partidarios y retractores…
Pero hoy vamos a darle un vistazo desde nuestro punto de vista, el de los programadores. Microsoft nos ha dado una gran ventaja a quienes usamos WPF / Silverlight, pues ahora también somos desarrolladores de Windows Phone 7, puesto que todas las aplicaciones para este S.O. se desarrollarán en XNA o Silverlight.
 
Esto es toda una revolución en comparación a Windows Mobile 6.X u otras plataformas móviles de la competencia,  pues nos permitirá portar aplicaciones que ya tengamos trabajando sobre Silverlight de una forma rápida y sencilla, casi sin reescribir código.
Para acercarnos un poco al desarrollo de una aplicación hecha en silverlight para Windows Phone 7 os traigo como ejemplo un lector de RSS (muy básico, no tiene aspiraciones comerciales ni mucho menos) en el que haremos uso de varias cosas interesantes de Silverlight y Windows Phone 7:

    • Application Bar

    • Acceso al almacenamiento local de nuestro terminal

    • Serialización y Deserialización de objetos

    • Uso de WebClient

    • Uso del control WebBrowser para mostrar HTML generado dinámicamente
El aspecto de nuestra aplicación:
Captura 2

 

Empezando

Para poder desarrollar aplicaciones para Windows Phone 7 necesitaremos descargarnos el CTP de las herramientas de desarrollo aquí
Este pack incluye Visual Studio 2010 Express for Windows Phone, por lo que si no tenemos Visual Studio 2010, con este pack no necesitaremos buscarlo por otras fuentes.
Una vez instaladas las Developers tools, solo tendremos que abrir Visual Studio 2010 y escoger un nuevo proyecto del tipo Silverlight for Windows Phone como se muestra en la imagen:
Captura3
Si, lo que ves es correcto, el proyecto está en Visual C#, no lo busques en Visual Basic, no lo encontraras, todavía. Por ahora solo existe soporte para C#, pero desde el equipo de Windows Phone han prometido que la versión final del Kit de desarrollo tendrá soporte para Visual Basic, y espero que sea cierto, soy más amigo de VB que de C#, aunque al final, realmente todo es .NET y los dos lenguajes no cambian tanto.

Application Bar

La application bar es una magnifica idea de diseño del equipo de Windows Phone 7 para ayudarnos a crear el interface de nuestras aplicaciones, lo puedes ver en detalle en esta captura:
Captura4
La barra inferior violeta con los 3 puntos es la application bar, al pulsar sobre los 3 puntos se despliega mostrando todas las opciones disponibles:
Captura5
Es muy util pues nos permite crear un menú que podemos incluir en cada página de la aplicación que queramos solo con añadir una referencia, pues la definición del menu se encuentra en el archivo app.xaml y el código asociado a cada opción en el archivo app.xaml.cs, veamos el código necesario para definir el xaml del application bar:
Primero en nuestro proyecto debemos incluir una referencia al ensamblado Microsoft.Phone.Shell
Después en nuestro app.xaml incluimos una referencia a Microsoft.Phone.Shell:
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;
assembly=Microsoft.Phone.Shell"


Por último dentro del Tag <Application.Resources> creamos nuestro Application Bar:


<shell:ApplicationBar x:Name="MenuAppBar" Opacity="0.7" 
IsVisible="True" IsMenuEnabled="True" 
BackgroundColor="Purple" >
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="Edit Rss Sources" 
x:Name="EditRss" 
Click="EditRss_Click"/>
<shell:ApplicationBarMenuItem Text="Update Local Rss" 
x:Name="UpdateRss" 
Click="UpdateRss_Click"/>
<shell:ApplicationBarMenuItem Text="View Local Rss" 
x:Name="ViewRss" 
Click="ViewRss_Click"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>  


Por último, en cada página en la que queramos que aparezca el Application Bar debemos indicarlo definiendo el Atributo ApplicationBar en la cabecera del objeto <navigation:PhoneApplicationPage>:


<navigation:PhoneApplicationPage 
...
...
ApplicationBar="{StaticResource MenuAppBar}">


Con esto si ejecutamos nuestra aplicación veremos como ya aparece nuestro Application Bar y se despliega mostrando todas las opciones.


Para añadir código a cada Opción, nos servimos del evento Click que definimos anteriormente en xaml, por ejemplo, la opción Edit Rss Sources llama al evento EditRss_Click, que está localizado en el archivo app.xaml.cs y tiene esta firma:


private void EditRss_Click(object sender, EventArgs e)
{
//Do something awesome here!
}


Como puedes ver la firma del método EditRss_Click es como la del evento Click de cualquier botón.


Acceso al almacenamiento local, serializar y deserializar objetos



Uno de los grandes cambios en el desarrollo entre Windows Mobile 6.X y Windows Phone 7 es que nuestras aplicaciones no tienen permiso para acceder al almacenamiento general del terminal sobre el que se almacenan y no podemos usar bases de datos en el propio terminal.


Para suplir esto la directriz de microsoft es clara: mueve tus datos a la nube y que tu aplicación acceda a ellos mediante su conexión a internet. Aunque esto es totalmente válido podemos encontrarnos ocasiones en que deseemos almacenar cierta información en el terminal, a modo de cache de datos o guardar preferencias de usuario sobre la aplicación y su estado. Esto podemos realizarlo accediendo al IsolatedStorage asignado a nuestra aplicación. Este espacio de almacenamiento está aislado del resto del sistema y aplicaciones, y es exclusivo para nuestra aplicación.


Pongamos por ejemplo que queremos guardar un archivo en este almacenamiento aislado que contenga todos los  Rss que hemos configurado en nuestra aplicación, esto es muy sencillo usando la serialización y deserialización xml y decorando nuestras clases con los atributos xml necesarios.


Para esto lo primero es que añadamos a nuestro proyecto una referencia al ensamblado System.Xml.Serialization.


Para empezar vamos a crear una clase que pueda almacenar la información que queremos guardar de cada Rss:


/* A simple class to store rss information. */
public class RssSource
{
//Public Properties:
[System.Xml.Serialization.XmlElement]
public String Description { get; set; }
[System.Xml.Serialization.XmlElement]
public String Url { get; set; }

//Constructors:
public RssSource()
{
}

public RssSource(String SourceDesc, String SourceUrl)
{
Description = SourceDesc;
Url = SourceUrl;
}
}


Como podéis ver es una clase muy sencilla, tiene dos propiedades públicas donde almacenaremos la url del Rss y una descripción y dos constructores.


Si os fijáis en las propiedades de esta clase veréis que ambas están decoradas con el atributo System.Xml.Serialization.XmlElement, esto sirve para que al serializar la clase y convertirla en Xml el serializador sepa en que se convierte cada propiedad.


Ahora, como queremos almacenar más de una fuente Rss en nuestro archivo, vamos a crear otra clase, que será la que serialicemos y además la que tendrá la funcionalidad de añadir, eliminar, guardar y cargar nuestra lista de fuentes:


/* A simple class to manage rss sources */
public class RssSources
{
//Public List with all rss sources.
[System.Xml.Serialization.XmlArray]
public List<RssSource> Sources = new List<RssSource>();

//Constructor.
public RssSources()
{

}

//Load rss sources saved to Isolated Storage.
public bool LoadFromIso()
{
XmlSerializer Serializator = new XmlSerializer(typeof(RssSources));
IsolatedStorageFile IsoFile = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream Stream = IsoFile.OpenFile("Sources.rss", FileMode.OpenOrCreate);
try
{
RssSources DiskSources = (RssSources)Serializator.Deserialize(Stream);
this.Sources = DiskSources.Sources;
return true;
}
catch (Exception)
{
return false;
}
finally
{
Stream.Close();
IsoFile.Dispose();
}

}

//Add new rss source to the list.
public bool AddSource(String Description, String Url)
{
try
{
Sources.Add(new RssSource(Description, Url));
SaveToIso();
return true;
}
catch (Exception)
{
return false;
}
}

//Delete rss source from the list.
public bool DeleteSource(RssSource Item)
{
try
{
Sources.Remove(Item);
return true;
}
catch (Exception)
{
return false;
}
}

//Save current List of rss sources to Isolated Storage
private bool SaveToIso()
{
XmlSerializer Serializator = new XmlSerializer(typeof(RssSources));
IsolatedStorageFile IsoFile = IsolatedStorageFile.GetUserStoreForApplication();
if (IsoFile.FileExists("Sources.rss")) 
{ 
IsoFile.DeleteFile("Sources.rss"); 
}
IsolatedStorageFileStream Stream = IsoFile.CreateFile("Sources.rss");
try
{
Serializator.Serialize(Stream, this);
return true;
}
catch (Exception)
{
return false;
}
finally
{
Stream.Close();
IsoFile.Dispose();
}
}
}


Bien, esta clase expone una lista de tipo RssSource (la clase base que creamos anteriormente), un constructor sin parámetros y cuatro métodos:



  • AddSource: Simplemente añade un nuevo RssSource a la Lista Sources.


  • DeleteSource: Elimina un Item existente de la Lista sources.


  • SaveToIso: Usando la clase XmlSerializer creamos un serializador del mismo tipo que nuestra clase, obtenemos el IsolatedStore de nuestra aplicación y creamos una IsolatedSorageFileStream, y por último serializamos el contenido de nuestra clase a este archivo.


  • LoadFromIso: La operación inversa al método SaveToIso, abrimos un archivo con nuestra clase serializada y la Deserializamos para obtener la Lista de RssSource que contiene.



Si examinas el código verás que es realmente sencillo y nos permite almacenar información de forma muy simple y rápida, por supuesto no puede sustituir a una base de datos, pero para pequeñas porciones de datos o para persistir a un medio fisico el estado de los objetos de nuestra aplicación, es muy útil.


Uso de WebClient



Bien, ya tenemos nuestras fuentes Rss almacenadas, pero, ¿Como obtenemos el contenido de las mismas? Pues muy sencillo, usando la clase WebClient de Silverlight


Esta clase nos permite descargar contenido en formato texto desde una dirección URI de forma asíncrona, por lo que lo primero que debemos hacer es declarar un método que responda al evento DownloadStringCompleted de la clase WebClient:


private void DownloadComplete(object sender, DownloadStringCompletedEventArgs e)
{
var client = sender as WebClient;
if (client != null)
{
client.DownloadStringCompleted -= DownloadComplete;
}
if (e.Error != null)
{
throw e.Error;
}
if (e.Result != null)
{
//e.Result contains a String with the downloaded data.
}
}


e.Result contiene el texto descargado por webclient, en nuestro caso es xml conteniendo las distintas noticias del rss que hemos descargado. En el proyecto de ejemplo uso Linq to Xml para obtener todas las noticias y guardarlas en una Lista genérica.


Una vez que hayamos definido el manejador del evento DownloadStringCompleted solo nos queda crear el objeto WebClient e indicarle que descargue el rss:


WebClient client = new WebClient();
client.DownloadStringCompleted += DownloadComplete;
client.DownloadStringAsync(new Uri(RssUrl));




Uso del control WebBrowser para mostrar HTML generado dinámicamente



El último paso es poder mostrar el contenido de las noticias, normalmente este contenido viene en formato html con tags de imagenes y otros, por lo que lo más apropiado es usar el control WebBrowser para mostrar el contenido en forma de página web, para ello solo necesitamos añadir el head y body a nuestra noticia y usar el método NavigateToString del control webBrowser para mostrar nuestro html:


webBrowser1.NavigateToString(
"<html><head><meta name='viewport' content='width=480, user-scalable=yes' /></head><body>" +
(lstDetails.SelectedItem as RssFeed).Description +
"</body></html>");


Con este simple código podremos mostrar en el control webbrowser cualquier cadena de texto como si de una página web se tratase.





Y esto es todo, a continuación os dejo el proyecto de ejemplo con la aplicación completa de descarga y visionado de Rss sin conexión, pues usando las técnicas que aquí os muestro se descarga todos los rss y los almacena en el terminal, por lo que pueden descargarse en casa desde la wifi y verlas en la sala de espera del médico, donde misteriosamente siempre hay tan poca cobertura y tanta gente delante de nosotros jeje.


Espero que os haya gustado, Happy Coding, un gran saludo a todos y gracias por leerme!

2 comentarios:

  1. Hola:
    ¿No falta un MainPage.xaml?
    No se me ejecuta

    Gracias, igualmente, un saludo

    ResponderEliminar
  2. Hola Rubén
    el problema es que este ejemplo es de las herramientas beta y no es compatible con la versión final , por eso no se ejecuta,

    Un saludo :)

    ResponderEliminar