Después de realizar la charla en el evento Talks4Kids sobre MvvmCross, me he decidido a escribir una serie de post (y quizás algo más), para explicar de forma concisa el uso de esta librería para desarrollar aplicaciones multiplataforma con Xamarin.
En un principio, me voy a ceñir en estos primeros post a explicar el uso de MvvmCross con Xamarin Classic. Es mas complejo de usar, pero quizás es la mejor manera de explicar y entender el uso. Pero lo primero es lo primero, que es MvvMCross.
MvvmCross es una librería de código abierto, creada por Stuart Lodge, allá por el año 2011. Parte de un proyecto anterior que se denominaba MonoCross y fundamentalmente es usada para dar soporte al framework MvvM dentro de aplicaciones desarrolladas con Xamarin, tanto en su versión Classic como Forms y aplicaciones nativas del ecosistema de Windows, tanto escritorio como móvil.
MvvmCross aporta casi todos los artefactos usados en cualquier librería o framework de Mvvm, así como la capacidad de binding sobre elementos tanto en proyectos Android como iOS. Debemos de recordar que en iOS por ejemplo el patrón de creación de aplicaciones es obligatoriamente MVC y en Android no existe un patrón, vamos no existe un patrón de facto, normalmente se usa el patrón ASM, aunque podemos encontrarnos también aplicaciones creadas según MVC como en iOS.
MvvmCross actualmente soporta proyectos de Xamarin.Android, Xamarin.iOS, Xamarin.Mac, WPF, Windows Store, Windows Phone y Universal Windows App.
La estructura básica de cualquier proyecto que use MvvmCross (también conocido como Mvx) se basa en el proyecto o proyectos de cada una de las plataformas, mas otro proyecto central (core) consistente en una librería de código portable o PCL. Por ejemplo, en una solución en la que tengamos un proyecto para desarrollar nuestra aplicación sobre Xamarin.Android, el aspecto mas o menos que nos podríamos encontrar de primeras al crear su estructura básica seria el siguiente:
Lo primero que debemos de realizar si queremos usar Mvx, es añadir a nuestros proyectos, (tanto los proyectos de plataforma como la PCL) los paquetes necesarios a través de Nuget. Estos paquetes están dentro de MvvmCross.HotTuna.MvvmCrossLibraries. Al instalarlo también se instalaran las librerías del core MvvmCross.HotTuna.CrossCore y MvvmCross.PortableSupport, de las cuales depende.
Actualmente se encuentra en fase beta la versión 4.0 de estas librerías. Todos los ejemplos y explicaciones de este y los siguientes post, a no ser que lo comente de forma explicita les crearé usando la versión 3.5.0.
Nota: Como curiosidad, todas las librerías que componen Mvx, tienen como nombre de espacios la denominación de Cirrious.Cirrious Ltd es la empresa que en 2008 creo Stuart Lodge y en la que comenzó a usar MvvmCross.
Bueno, ya tenemos nuestro proyecto para Android en este caso y nuestra PCL. A parte hemos añadido mediante Nuget las librerías necesarias para usar Mvx. Y ahora que hacemos?
Clase App.cs
Lo primero es crear (tal como se ve en la imagen de la solución inicial mas arriba), una clase principal, normalmente denominada App.cs, dentro de nuestro proyecto PCL, que hereda de MvxApplication. MvxApplication se encuentra dentro del nombre de espacios Cirrious.MvvmCross.ViewModels.
MvxApplication, es una clase que implementa el patrón Singleton y será la encargada de ayudarnos con el ciclo de vida de nuestras ViewModels, servicios, modelos, etc.. Ojo, no debemos de confundir esta clase, con los objetos ApplicationDelegate en iOS o Application en Android y Windows, que son los encargados de gestionar el ciclo de vida de la aplicación.
Una vez creada la clase, una de las tareas a realizar es el registro de una clase que implementa la interfaz IMvxAppStart, mediante el método RegisterAppStart. Esto se puede realizar por ejemplo al sobrescribir el método Initialize, aunque existirían otros lugares en donde se podría realizar. De momento nuestra clase App es muy sencilla, vemos como queda finalmente:
namespace MvxExample.Core
{
using Cirrious.MvvmCross.ViewModels;
public class App : MvxApplication
{
public override void Initialize()
{
RegisterAppStart<ViewModels.StartViewModel>();
}
}
}
Nota: Doy por sentado que para seguir este post y los que vendrán, sabes usar MVVM, que significan cada una de las clases y mas o menos cuales son cada una de las partes que lo conforman. Si no fuera el caso, es necesario conocer todo esto, para ello puedes comprar mi libro sobre desarrollo para Windows 8, en el que existen 2 grandes capítulos dedicados a este patrón, visualizar algún video como por ejemplo este en donde explicamos el patrón en los hangouts de Desarrollo Web o leer algún que otro post explicándolo todo.
View Model StartViewModel.cs
Ok, ya tenemos nuestra clase principal, App, que lanza y registra una IMvxAppStart. Que es esto. Pues básicamente la ViewModel o ViewModels (porque podremos asignar mas de una) que se mostraran en el inicio de la aplicación.
Para crear esta o cualquier otra ViewModel, debemos de crear una clase que implemente MvxViewModel del nombre de espacio Cirrious.MvvmCross.ViewModels. La implementación de MvxViewModel, nos aporta la infraestructura como si de una ViewModel Base se tratara. Para este primer ejemplo crearemos esta ViewModel, con una sencilla propiedad, la cual por supuesto implementa notificación de cambios.
namespace MvxExample.Core.ViewModels
{
using Cirrious.MvvmCross.ViewModels;
public class StartViewModel : MvxViewModel
{
private string initialText;
public StartViewModel()
{
InitialText = "Hello MvvmCross";
}
public string InitialText
{
get { return this.initialText; }
set
{
this.initialText = value;
RaisePropertyChanged(() => InitialText);
}
}
}
}
Clase inicial, App, una ViewModel, StartViewModel. Ok, esto va tomando forma, ahora lo que necesitamos es configurar nuestro proyecto de plataforma (en el ejemplo el proyecto Android), para usar toda esta infraestructura. Para ello usando convencionalismos de nombres necesitaremos crear en el proyecto una clase que se denomine Setup.cs.
Nota: MvvmCross esta lleno de convencionalismos de nombres. Debemos de acostumbrarnos a que las clases y demás artefactos necesarios deben de tener unos nombres concretos. Por ejemplo, de no indicar lo contrario por defecto, nuestras ViewModels y nuestras vistas se van a enlazar si tienen el mismo nombre, como veremos mas adelante.
Clase Setup.cs
La clase Setup, sirve de entrada e instalación del sistema de MvvmCross dentro de cada una de las plataformas. Dentro de ella podremos realizar un montón de pasos, todos ellos encaminados en la personalización de MvvmCross. En esta primera aproximación, vamos a dejar nuestra clase Setup del proyecto de Android reducida a la mínima expresión. Ya entraremos en modificarla y añadir una personalización mas profunda en próximos capítulos.
Toda clase Setup de cada una de las plataformas debe de implementar una clase homónima de la plataforma correspondiente. MvxStoreSetup, en el caso de proyectos WindowsStore, MvxPhoneSetup, para Windows Phone, MvxWpfSetup en WPF, MvxTouchSetup para iOS y MvxAndroidSetup en nuestro caso concreto que tenemos un proyecto Android.
namespace MvxExample.Android
{
using Cirrious.MvvmCross.Droid.Platform;
using Cirrious.MvvmCross.ViewModels;
using global::Android.Content;
public class Setup : MvxAndroidSetup
{
public Setup(Context applicationContext) : base(applicationContext)
{
}
protected override IMvxApplication CreateApp()
{
return new MvxExample.Core.App();
}
}
}
Como comentamos la clase Setup, se encarga por un lado de recibir el contexto, root frame, UIWindow o lo que sea menester en cada plataforma y crear el objeto App implementado en la clase que hemos visto anteriormente.
Como podemos ver en el código superior, instanciamos el constructor de la clase, pasando el contexto de Android a la clase Base MvxAndroidSetup y por otro lado sobrescribimos el método CreateApp para retornar el objeto (recordamos que es un Singleton) App, de nuestra PCL.
De momento, como decimos es lo mínimo necesario para que nuestra aplicación Android, implemente la clase App, necesaria para el control del ciclo de vida de nuestras ViewModels y sea la encargada de realizar la magia necesaria para que todo esto funcione.
Como toma de contacto y para que nuestro circulo esté totalmente dispuesto para funcionar, es necesario implementar la clase Setup en algún lado y lanzar nuestra clase App. En cada una de las plataformas se realiza de una forma diferente, pero veremos ahora como realizarlo en Android, y en un próximo post, ya repasaremos plataforma a plataforma la forma de implementación.
Splash Screen
Como siempre existen varias formas de realizarlo pero en Android una de las mas sencillas es la creación de una Activity que implemente la clase MvxSplashScreenActivity.
Basta con indicar en el constructor cual es el fichero axml del layout y lo mas importante de todo indicar en las propiedades de la Activity que es el MainLauncher. Recordar que solo debe de existir un MainLauncher en toda la aplicación.
namespace MvxExample.Android
{
using Cirrious.MvvmCross.Droid.Views;
using global::Android.App;
[Activity(Label = "Splash Screen", MainLauncher = true,
Icon = "@drawable/icon")]
public class SplashScreen : MvxSplashScreenActivity
{
public SplashScreen() : base(Resource.Layout.SplashScreen)
{
}
}
}
Vistas.
Bueno pues como colofón final, simplemente nos quedaría crear la vista a la cual vamos a llamar en primer lugar. Recordamos que cuando hemos creado en la PCL nuestra clase principal de MvvmCross llamada App le indicamos que la ViewModel principal seria StartViewModel. Pues bien, vamos ahora a crear la vista que enlaza con dicha ViewModel.
En Android las vistas son un conjunto de 2 ficheros. Por un lado una clase que implementa MvxActivity. En este caso por facilitar la comprensión de nuestro proyecto, la vamos a ubicar dentro de una carpeta denominada Views y se llamará igual que la ViewModel que la usará pero sin la palabra Model, es decir en este caso tendrá el nombre de StartView (recordad lo que hemos comentado sobre los convencionalismos de los nombres, para que se haga magia entre todas las piezas).
namespace MvxExample.Android.Views
{
using Cirrious.MvvmCross.Droid.Views;
using global::Android.App;
using global::Android.OS;
[Activity(Label="Start View")]
public class StartView : MvxActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.StartView);
}
}
}
Al igual que la StartScreen esta clase es una Activity a la que únicamente la debemos de indicar cual es su layout. mediante el uso del método SetContentView, le proporcionamos el id del mismo. Seguimos con convencionalismos y al layout le llamaremos de igual forma StartView.
Binding.
Al igual que creamos el fichero axml para el layout de nuestra StartScreen, crearemos el mismo para esta vista. Como hemos comentado anteriormente le llamaremos StartView.axml. En este caso va a tener un poco mas de chicha que la StartScreen y mostraremos el contenido de la propiedad InitialText que hemos creado en su ViewModel.
Para ello, lo primero que debemos de hacer es crear un nuevo namespace, que normalmente es definido como “local”, que apunte a la dirección “http://schemas.android.com/apk/res”, añadiendo el namespace de nuestro proyecto, y por supuesto crear un elemento TextView en donde mostraremos el texto.
Aquí es donde vamos a comenzar a usar los bindings característicos de Mvvm. En nuestra etiqueta TextView, crearemos una nueva propiedad referenciada sobre el nuevo namespace “local” creado anteriormente y “MvxBind”. En su valor indicaremos que queremos bindear, la propiedad Text del TextView con la propiedad InitialText de su ViewModel. > local:MvxBind=”Text InitialText”
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/MvxExample.Android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="40dp"
local:MvxBind="Text InitialText" />
</LinearLayout>
Para que podamos usar estas propiedades y podamos realizar los bindings en Android es necesario crear un fichero de recursos en donde le indicaremos la definición de las etiquetas de atributos “MvxBind” y alguna otra. Para ello crearemos el fichero XML, el cual denominaremos “MvxBindingAttributes.xml” y el que colocaremos en la correspondiente carpeta “values”. El contenido completo de este fichero es el siguiente:
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="MvxBinding">
<attr name="MvxBind" format="string"/>
<attr name="MvxLang" format="string"/>
</declare-styleable>
<declare-styleable name="MvxControl">
<attr name="MvxTemplate" format="string"/>
</declare-styleable>
<declare-styleable name="MvxListView">
<attr name="MvxItemTemplate" format="string"/>
<attr name="MvxDropDownItemTemplate" format="string"/>
</declare-styleable>
<item type="id" name="MvxBindingTagUnique"/>
<declare-styleable name="MvxImageView">
<attr name="MvxSource" format="string"/>
</declare-styleable>
</resources>
Con todos los ficheros que hemos creado, finalmente nuestra solución queda de la siguiente forma.
Bueno, pues ya tenemos todo lo necesario para usar MvvmCross en un proyecto Android con Xamarin. Podemos crear nuestras ViewModels y enlazarlas mediante bindings a sus correspondientes vistas (recordad que a no ser que indiquemos lo contrario, el enlace entre ViewModel y View es por convencionalismo de nombres). En posteriores post, veremos como a parte de bindear propiedades, podemos crear comandos, usar servicios y mil y una cosa mas.
Como resumen, os muestro a continuación un esquema del ciclo de vida de la app Android que acabamos de crear.
Espero que os haya servido de ayuda como primera aproximación al uso de MvvmCross, puede parecer un poco lioso, pero como casi cualquier cosa, una vez que lo entendemos y lo dominamos la gran ventaja de usarlo en nuestros desarrollos multiplataforma con Xamarin es brutal.
Aquí os dejo el código de ejemplo de este primer proyecto.
Nos vemos.