Extendiendo el código generado en Windows Phone App Studio

Introducción

Previamente ya habíamos hablado de Windows Phone App Studio, nueva herramienta online que permite crear aplicaciones de calidad para Windows Phone 8 en cuestión de minutos.Entre sus principales características hay muchas destacables:

  • Permite crear aplicaciones Windows Phone sin tener conocimientos de desarollo.
  • Las Aplicaciones se generan en código nativo.
  • Permite instalar directamente la Aplicación sin requerir cuentas extras de ningun tipo.
  • Permite compartir la Aplicación creada con amigos.

App Studio

Sin embargo, la característica principal del sistema, es que puedes descargar el código fuente
de la Aplicación. Esto permite extender el código fuente añadiendo
nuevas características. En esta entrada vamos a analizar la estructura y
técnicas utilizadas en el código generado asi como aprener como
extenderlo con facilidad.

¿Te apuntas?

Echemos un vistazo al código…

Una vez generamos nuestra Aplicación desde Windows Phone App Studio veremos una pantalla similar a la siguiente:

Descargar el código fuente

Desde este apartado podemos:

  • Instalar nuestra Aplicación en un dispositivo para probarla de manera fácil leyendo un simple código QR.
  • Compartir vía email nuestra Aplicación con amigos.
  • Descargar el paquete de publicación (el XAP ya empaquetado listo para publicar).
  • Y por último, podemos descargar el código fuente!

Descargamos el código fuente de nuestra Aplicación:

Descomprimimos y abrimos la solución (Solution.sln) en Visual Studio.

NOTA: El proyecto es una solución para Windows Phone 8 que requiere Visual Studio 2012.

De un primer vistazo a la estructura de la solución podemos ver que la solución esta organizada en diferentes proyectos:Estructura

WP8App: Este proyecto es la Aplicación Windows Phone 8 en si. Implementa el patrón MVVM junto a otras técnicas y buenas prácticas (Ioc, servicios, etc) de los que hablaremos con calma más adelante.
Entities: Entidades utilizadas en la Aplicación. Las entidades implementan una iterfaz BindableBase que es una implementación de la interfaz INotifyPropertyChanged.
Repositories: En este proyecto tenemos disponibles todos repositorios de información utilizados por la Aplicación.

Esta
separación de responsabilidades en proyectos independientes nos permite
extender con mayor facilidad cada una de las partes implicadas asi como
la creación de test unitarios que se encargen de asegurar la calidad y
funcionamiento de todo.

 

Profundizamos en la estructura del proyecto Windows Phone:

Lo primero que llama la atención a simple vista es la implementación del patrón MVVM en el proyecto. Model-View-ViewModel (MVVM) es un patrón de diseño de aplicaciones que permite desacoplar el código de interfaz de usuario del código que no sea de interfaz de usuario.

El patrón MVVM se compone de tres partes fundamentales:

La Vista (View):
Define las vistas de la aplicación, es decir, conjunto de controles,
layout, estilos, etc. Se nutre del vista-modelo utilizando su propiedad DataContext.
Los controles se configuran mediante propiedades expuestas por el
vista-modelo e interactuan mediante el uso de comandos. Todas las vistas
están situadas dentro de la carpeta View.
El Vista-Modelo (ViewModel):
Conjunto de clases que encapsulan la lógica de presentación. Implementa
propiedades y comandos a los que se asociará la vista. Controla la
interacción de la vista con el modelo. Todos los viewmodels están
organizados dentro de la carpeta ViewModel.
El Modelo (Model):
Conjunto de clases que encapsulan los datos de la aplicación junto a su
lógica de negocio. Validan el modelo de los datos aplicando reglas de
negocio. El modelo suele implementar las interfaces INotifyPropertyChanged y INotifyCollectionChanged
para notificar cambios en propiedades y colecciones. No deben
referenciar a la vista ni al vista-modelo. En nuestra solución, tenemos
los modelos dentro del proyecto Entities (recordar que implementan la interfaz) .

A vista de pájaro ya hemos analizado la estructura del proyecto, sin embargo, creo que merece la pena que pronfundizemos más.

Con
el objetivo en mente se poder extender y reutilizar el código en la
medida de lo posible, se evita añadir código específico de la plataforma
en los viewmodels. No podemos utilizar directamente APIs de Windows
Phone en nuestros viewmodels.

¿Porque?

Hay varios motivos
para ello. Por ejemplo, si queremos portar la Aplicación a Windows 8
utilizaremos una Portable Library. Los viewmodels son candidatos
perfectos para migrar a la clase portable pero no podemos hacerlo con
código específico de la plataforma.

¿Cómo lo conseguimos?

Las operaciones que necesitemos en nuestra aplicación que requieran acceder a las APIs de Windows Phone las implementaremos en servicios. Podemos encontrar los servicios en la carpeta Services del proyecto.

Dependiendo
de los extras y otro tipo de configuraciones aplicadas en el portal
durante el proceso de creación de la Aplicación, podemos contar con los
siguientes servicios:

DialogService. Lo utilizamos para mostrar notificaciones en nuestra aplicación mediante Messagebox.
LiveTileService. El nombre deja pocas dudas. Lo utilizamos para la gestión de tiles en la aplicación, crear y eliminar tiles secundarios.
NavigationService. Utilizamos el servicio NavigationService para realizar la navegación entre páginas.
ReminderService. Nos permite crear recordatorios.
ShareService. Nos permite compartir información (Lanzador EmailComposeTask).
LockScreenService. Nos permite modificar la imagen utilizada en la LockScreen.
SpeechService. Nos permite utilizar TTS en nuestra Aplicación.
WebBrowserService. Abre una URL en el navegador (Lanzador WebBrowserTask).

Los viewmodels implementan los servicios gracias al uso de Ioc (Inversion of Control) por DI
(Dependency Injection). Se crea un contenedor donde registramos todos
los servicios que vamos a utilizar junto a los viewmodels que utilizarán
las vistas y que accederán a los servicios utilizando interfaces.

Para ello, se utiliza Unity v2.1,
el contenedor IoC de Patterns & Practices. Contamos con un service
locator (ViewModelLocator) que utilizará el contenedor creado. Está
instanciado en App.xaml y se utiliza para que cada vista pueda acceder a
la instancia de su viewmodel correspondiente cada vez que lo necesite.
Los viewmodels a su vez  accederán a los servicios utilizando
interfaces.

Llegados a este punto y tras analizar la estructura y
técnicas utilizadas en el código generado de nuestra aplicación podemos
deducir que:

  • El código generado implementa el patrón MVVM y
    utiliza conceptos y buenas prácticas como Ioc, servicios o la
    abstracción de implementaciones gracias a interfaces creando un código de calidad, además facilmente extensible.
  • El
    código viene en líneas generables perfectamente preparado para
    implementar test con facilidad o utilizar Portable Library y migrar
    nuestra aplicación a otras plataformas.
  • Para enriquecer nuestras
    vistas se utilizan algunas de las herramientas más conocidas en el
    entorno de desarrollo Windows Phone como el Toolkit o MyToolkit. Para 
    facilitar la gestión de dichas librerías se incluyen paquetes NuGet.

Asi que, analizada la estructura del código generado, ¿que tal si lo extendemos?

Extendiendo el código

Manos a la obra. Vamos a extender el código de la Aplicación generada. Para probar las múltiples opciones a realizar, vamos a:

  • Modificar uno de los servicios ya existentes para modificar la implementación del mismo.
  • Añadir un nuevo servicio que añada nueva funcionalidad a al Aplicación.

Comenzamos modificando uno de los servicios existentes. Nos centramos por ejemplo en el servicio DialogService  que utilizamos para mostrar notificaciones en nuestra aplicación mediante Messagebox:

public class DialogService : IDialogService
{
public void Show(string message)
{
MessageBox.Show(message);
}

public void Show(string message, string caption)
{
MessageBox.Show(message, caption, MessageBoxButton.OK);
}
}

Contamos
con las librerías del Windows Phone Toolkit incluidas en el proyecto.
Por lo tanto, podemos extender el servicio utilizando un control más
versátil como el CustomMessageBox perteneciente al Toolkit. Para ello,
en la misma clase, añadimos la referencia necesaria para poder utilizar
el control:

using Microsoft.Phone.Controls;

Reemplazamos la lógica de cada método:


public void Show(string message)
{
CustomMessageBox messageBox = new CustomMessageBox()
{
Message = message
};

messageBox.Show();
}

public void Show(string message, string caption)
{
CustomMessageBox messageBox = new CustomMessageBox()
{
Caption = caption,
Message = message
};

messageBox.Show();
}

Fácil,
¿verdad?. Por supuesto, podemos añadir más métodos al servicio
(recordar en dicho caso incluirlas en la interfaz) permitiendo confgurar
el botón derecho, el botón izquierdo, el contenido del mensaje (en este
control tenemos muchas posibilidades como por ejemplo incluir hasta un
Pivot como contenido), etc.

De acuerdo, hemos modificado algo
bastante simple. ¿Que pasa si deseamos añadir nueva funcionalidad a la
ya existente?. No dista mucho de lo ya realizado. Vamos a añadir un
nuevo servicio. Una acción bastante común en Aplicaciones Windows Phone
que acceden a información alojada en un servidor suele ser cachear la
información. Para ello, una buena opción es utilizar el Isolated
Storage.

Como el acceso al Isolated Storage es una funcionalidad
específica de la plataforma… si, efectivamente, va a los servicios.
Creamos entonces un servicio llamado StorageService.

Creamos la interfaz que define al servicio:

public interface IStorageService
{
T Load<T>(string fileName);
bool Save<T>(string fileName, T data);
}

Como
podéis ver muy sencilla. Tendrá un método Load que pasado un  nombre de
achivo devolverá la información almacenada y otro método Save que
tendrá como parámetros el nombre del archivo junto a la información a
almacenar.

Implementamos la interfaz:

public class StorageService : IStorageService
{

}

Creamos la clase que implementa la interfaz:

public T Load<T>(string fileName)
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!myIsolatedStorage.FileExists(fileName))
return default(T);

using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile(fileName, FileMode.Open))
{
var xml = new XmlSerializer(typeof(T));
T data = (T)xml.Deserialize(stream);
return data;
}
}
}

public bool Save<T>(string fileName, T data)
{
try
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(fileName))
{
var xml = new XmlSerializer(typeof(T));
xml.Serialize(fileStream, data);
}
}

return true;
}
catch
{
return false;
}
}

El siguiente paso necesario será el registrar este servicio en nuestro contenedor de Ioc:

_currentContainer.RegisterType<IStorageService, StorageService>();

Por último, podemos utilizar nuestro servicio en un viewmodel por ejemplo muy facilmente:

private IStorageService _storageService;

En el contructor instanciamos los servicios:

public TheTeam_AlbumViewModel(IDialogService dialogService, INavigationService navigationService,
ILockScreenService lockScreenService, ITheGuysCollection theGuysCollection,
IawardsCollection awardsCollection, IvideosDataSource videosDataSource,
IblogDataSource blogDataSource, IStorageService storageService)
{
_dialogService = dialogService;
_navigationService = navigationService;
_lockScreenService = lockScreenService;
_theGuysCollection = theGuysCollection;
_awardsCollection = awardsCollection;
_videosDataSource = videosDataSource;
_blogDataSource = blogDataSource;
_storageService = storageService;
}

Y lo podemos utilizar por ejemplo, al obtener una colección de elementos:

_storageService.Save<ObservableCollection<RssSearchResult>>("Blog_NewsListControlCollection", Blog_NewsListControlCollection);

De
esta forma podemos almacenar en el almacenamiento aislado la
información del servidor. Si el usuario vuelve inmediatamente a ver la
misma información pdríamos recueperar la existente en lugar de volver a
realizar la petición al servidor.

Podéis echarle un vistazo al código generado (asi como las extensiones realizadas) descargando el ejemplo disponible desde el siguiente enlace.

En definitiva, el tener acceso al código nativo nos permite extender el código a nuestras propias necesidades.

Conclusiones

El
código generado implementa el patrón MVVM y utiliza conceptos y buenas
prácticas como Ioc, servicios o la abstracción de implementaciones
gracias a interfaces creando un código de calidad que como hemos podido verificar es facilmente extensible.

En
las plantillas de Visual Studio para Windows Phone se está extendiendo
el uso del patrón MVVM junto a otras buenas prácticas. Que el código
generado por Windows Phone App Studio haga uso de todas las buenas
prácticas y recomendaciones en el desarrollo Windows Phone es un punto
positivo que permite no solo ayudar a cualquier usuario a crear su
Aplicación para Windows Phone sino contribuir también en el aprendizaje
en el desarrollo de la plataforma mostrando los beneficios de estas
técnicas desde el principio.

Más información

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *