Hola MVVM(Model View ViewModel) PCL, Xamarin.Android, Xamarin.iOS, WP8.1 & W8.1



 Working with MVVM Pattern on Xamarin and Windows Projects


El modelo de desarrollo MVVM fui introducido hace algunos años por microsoft inicialmente para el desarrollo en tecnologias WFP, Silverlight y Windows Phone, en que consiste este modelo mas que nada en el desarrollo de interfaces graficas responsivas a variables y acciones creadas en codigos sin la necesidad de instancear o crear un modelo que se comunique con la clase si no desde un archivo AXML o de interfaz grafica comunicarse directamente con nuestro codigo sin la necesidad de crear un puente entre ellos.

Si eres nuevo con el MVVM puedes dar una revision a este enlace con algo de teoria acerca del modelo http://www.codeproject.com/Articles/100175/Model-View-ViewModel-MVVM-Explained

Con esta apliacion ejemplo se agrega la posibilidad de crear interfaces unificadas aprovechando el uso de librerias portables y el modelo MVVM para la realizacion de interfaces responsibas a acciones y actualizacion de valores de nuestro modelos.

PCL

Primero agregaremos una libreria tipo PCL en la cual tendremos nuestros modelos y acciones para comunicarse con la interfaz grafica la configuracion de la PCL sera la siguiente:


Primero agregaremos el siguiente code snippet que nos dara la funcion de una clase de ayuda para instanciar acciones con propiedas como acciones y estado de las acciones la clase se llamara "DelegateCommand.cs" y el codigo es el siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MvvmXamarin
{
    public class DelegateCommand : ICommand
    {
        private readonly Action _handler;
        private bool _isEnabled = true;
        public DelegateCommand(Action handler)
        {
            _handler = handler;
        }
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set {
                _isEnabled = value;
                if (CanExecuteChanged != null)
                {
                    CanExecuteChanged(this,EventArgs.Empty);
                }
            }
        }
        public bool CanExecute(object parameter)
        {
            return IsEnabled;
        }
        public event EventHandler CanExecuteChanged;
        public void Execute(object parameter)
        {
            _handler();
        }
    }
}

Seguido de esto crearemos un carpeta con el nombre "ViewModels" dentro de ella agregaremos nuestra clase tipo  INotifyPropertyChanged en la cual estaremos realizando nuestro Code Behind el nombre de la clase sera el siguiente "Model.cs" y contendra el siguiente codigo:

using System;
using System.Windows.Input;
using System.ComponentModel;
using System.Threading;

namespace MvvmXamarin
{
 public class Model: INotifyPropertyChanged
 {
  //Contexto
  public readonly SynchronizationContext context;
  //Constructor
  public Model()
  {
   //Se inicializa el contexto
   context = SynchronizationContext.Current;
  }

  //Nuestra propiedad text esta propiedad sera tomada graficamente como una Label, Etiqueta
  private string text="Write Here";
  //accesor get,set de la propiedad text
  public string Text {
   get
            {
                return text;
            }
   set
   {
    text = value;
    //Despues de asignado el valor disparamos el evento de cambio de pripiedad
    OnPropertyChanged("Text");
   }
  }

  //Variable de numero de veces
        private int Times = 0;
  //variable de texto de boton
        private string timesstring="Click";
  //Accesor get,set de nuestro texto de boton
        public string TimesString
        {
            get {
                return timesstring;
            }
            set {
                timesstring = value;
    //Despues de asignado el valor disparamos el evento de cambio de pripiedad
                OnPropertyChanged("TimesString");
            }
        }
  //Accion(Comando) del boton
        private ICommand clickEvent;
  //Accesor get, set de la accion del boton
        public ICommand ClickEvent
        {
            get {
    //retornamos nuestro evento en caso de ser nulo generamos una instacia de nuestra clase DelegateCommand
    //en la cual pasamos de parametro nuestro evento de click en este caso ExecuteClickCommand()
                return clickEvent ?? new DelegateCommand(ExecuteClickCommand);
            }
  }
  //Evento de click
        private void ExecuteClickCommand()
        {
   //Aumentamos +1 el valor de nuestra variable Times
           Times++;
   //Asignamos un nuevo valor a nuestra variable TimesString para disparar el evento de cambio de propiedad
           TimesString = string.Format("Clicked {0}",Times);
            
        }
  //evento PropertyChanged heredado de la clase INotifyPropertyChanged
  public event PropertyChangedEventHandler PropertyChanged;
  //Metodo OnPropertyChanged(string Name) heredado de la clase INotifyPropertyChanged
  public void OnPropertyChanged(string Name)
  {
   //revisamos que nuestra accion no sea nula.
   if (PropertyChanged == null)
    return;
   //Hacemos un envio al contexto indicando que se hizo un cambio de propiedad
   context.Post((s)=>{
    PropertyChanged(this, new PropertyChangedEventArgs((string)s));
   },Name);
  }
 }
}

Una vez agregada estas dos clases tendremos realizada nuestra libreria la cual quedara con una estructura como esta.



WINDOWS PHONE 8.1

Acontinuacion procederemos a agregar una aplicacion tipo Windows Phone 8.1 y a este proyecto referenciaremos nuestra libreria PCL





En nuestra interfaz grafica agregaremos los siguientes componenetes
  • 1 TextBlock
  • 1 TextBox
  • 1 Button
Nuestro codigo axml dentro del grid quedara asi:

    <Grid>
        <!--
        El valor de la propiedad Text de la label tendra un binding hacia nuestra variable Text dentro de nuestra PCL
        -->
        <TextBlock Text="{Binding Text}" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Height="56" Width="380" FontSize="36"/>
        <!--
        El valor de la priedad Text del textbox tendra un binding a nuestra Varible Text con asignando dos propiedades mas
        1 Mode=TwoWay indica que tendra las dos funciones asignacion y lectura
        2 UpdateSourceTrigger=PropertyChanged indica que lanzara un evento de actualizacion de propiedad cada que se modifique su valor
        -->
        <TextBox Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="10,90,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="380" Height="53"/>
        <!--
        El valor de la propiedad Content de nuestro boton tendra un binding hacia la varable TimesString
        asi como la propÌedad Command a nuestro Comando ClickEvent ambos dentro de nuestra PCL
        -->
        <Button Content="{Binding TimesString}" Command="{Binding ClickEvent}" HorizontalAlignment="Left" Margin="10,159,0,0" VerticalAlignment="Top" Width="380"/>
    </Grid>

Listo ahora como paso final dentro de nuestro constructor de nuestra clase MainPage indicaremos cual sera el contexto de la clase el cual sera una nueva instacia de nuestra clase Model quedando de la siguiente forma

        public MainPage()
        {
            this.InitializeComponent();

            //Asignamos una nueva instancia de nuestra clase Model al contexto de la vida
            DataContext = new Model();
            
            this.NavigationCacheMode = NavigationCacheMode.Required;
        }

y listo ahora podemos probarla ya tenemos nuestra aplicacion lista y funcionando si cambiamos el valor de la caja de texto nuestra label actualizara su valor automaticamente asi como nuestro boton por cada click que demos ira actualizando su valor.

WINDOWS 8.1 STORE APP

Al igual que nuestra aplicacion de WP8.1 nuestra interfaz estara realizada de la misma manera debido que las intefaces axml nos hace un enlace directo con nuestra clase.
Añadimos la referencia de nuestra PCL a nuestra app


En nuestra interfaz grafica agregaremos los siguientes componenetes
  • 1 TextBlock
  • 1 TextBox
  • 1 Button
Nuestro codigo axml dentro del grid quedara asi identico al de WP8.1:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock HorizontalAlignment="Left" Margin="33,34,0,0" TextWrapping="Wrap" Text="{Binding Text}" VerticalAlignment="Top" Height="85" Width="367" FontSize="24"/>
        <TextBox HorizontalAlignment="Left" Margin="33,152,0,0" TextWrapping="Wrap" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="367" Height="114" FontSize="24"/>
        <Button Command="{Binding ClickEvent}" HorizontalAlignment="Left" Content="{Binding TimesString}" Margin="30,284,0,0" VerticalAlignment="Top" Width="373" />
    </Grid>

Al igual que en Windows Phone hay que indicar en nuestra clase MainPage el contexto que se usara quedando nuestro constructor de clase de la siguiente manera:

        public MainPage()
        {
            this.InitializeComponent();

            //Asignamos una nueva instancia de nuestra clase Model al contexto de la vida
            DataContext = new Model();
        }

Listo nuestra aplicacion de W8.1 ya deberia de funcionar a la perfeccion:


Debido a que las aplicaciones de microsoft tiene soporte completo para el modelo MVVM se puede aprevechar de manera mas comoda este modelo a continuacion seguiremos con las aplicaciones de Xamarin.Android y Xamarin.Ios para aprovechar de mejor manera este modelo de desarrollo.

XAMARIN.ANDROID 
Agregamos la referencia a nuestra libreria PCL:



Al igual que en los anteriores proyectos crearemos una interfaz grafica sencilla con
  • 1 TextView
  • 1 EditText
  • 1 Button
Quedando nuestro axml de la siguiente manera:



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/Text" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/Field" />
    <Button
        android:text="Button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/Button" />
</LinearLayout>

Debido a que Xamarin no contiene soporte directo para hacer bindings desde la interfaz axml a menos de que se use algun tipo de framework como mvvmcross o mvvmlight debemos de hacer algunos fragmento de codigo dentro de nuestra activity principal con el fin de poder aprovechar el modelo MVVM el codigo de la actividad principal quedaria de la siguiente manera:

 
using System;

using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace MvvmXamarin.Android
{
 [Activity (Label = "MvvmXamarin.Android", MainLauncher = true)]
 public class MainActivity : Activity
 {
  //Contexto
  Model viewmodel;
  protected override void OnCreate (Bundle bundle)
  {
   base.OnCreate (bundle);
   //Se inicializa el contexto
   viewmodel = new Model ();

   // Set our view from the "main" layout resource
   SetContentView (Resource.Layout.Main);

   // Get our button from the layout resource,
   // and attach an event to it
   //Creamos nuestros objetos graficos
   var et = FindViewById<EditText> (Resource.Id.Field);
   var lbl = FindViewById<TextView> (Resource.Id.Text);
            var btn = FindViewById<Button>(Resource.Id.Button);
   //en nuestro boton hacemos directamente la llama a nuestro metodo
            btn.Click += (sender, e) => viewmodel.ClickEvent.Execute(null);
   //en nuestro evento textchanged de nuestro campo de texto actualizamos el valor de la variable Text de nuestro contexto
   et.TextChanged += (sender, e) => {
    viewmodel.Text = e.Text.ToString();
   };

   //Asignamos el evento PropertyChanged de nuestra variable contexto el cual nos retornara la accion realiza para asi hacer responsiva nuestra UI
   viewmodel.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => 
   {
    //Verificamos cual de las propiedades fue actualizada
                if (e.PropertyName == "Text")
                    lbl.Text = viewmodel.Text;
                else if (e.PropertyName == "TimesString")
                    btn.Text = viewmodel.TimesString;
   };
  }
 }
}

y asi de facil ya tendremos nuestra aplicacion Xamarin.Android funcionando correctamente:


XAMARIN.IOS

Por ultimo y no menos importante crearemos nuestra aplicacion de Xamarin.iOS

Antes que nada añadiremos la referencia de nuestra PCL:





Nuestra interfaz contara con:
  • 1 UILabel
  • 1 UITextView
  • 1 UIButton
Nuestra UI la realizaremos directamente en codigo quedando nuestro UIViewController de la siguiente manera:

using System;
using System.Drawing;

using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace MvvmXamarin.iOS
{
 public partial class MainViewController : UIViewController
 {
  public MainViewController () : base ()
  {
  }

  public override void DidReceiveMemoryWarning ()
  {
   // Releases the view if it doesn't have a superview.
   base.DidReceiveMemoryWarning ();
   
   // Release any cached data, images, etc that aren't in use.
  }
  //Contexto
  Model viewModel=null;
  public override void ViewDidLoad ()
  {
   base.ViewDidLoad ();
   //Agregamos color a nuestro fondo
            View.BackgroundColor = UIColor.Gray;

   // Perform any additional setup after loading the view, typically from a nib.
   //Se inicializa el contexto
   viewModel = new Model ();

   //Creamos nuestra Label
   UILabel lbl = new UILabel (new RectangleF(0,0,320, 50));
   //Asignamos el texto de la label
   lbl.Text = viewModel.Text;
   //Asignamos la propiedad de autoajust de la label
   lbl.AdjustsFontSizeToFitWidth = true;

   //Creamos nuestra caja de texto
   UITextView et = new UITextView (new RectangleF (0, 60, 320, 50));
   //Asignamos el texto de nuestra caja
   et.Text = viewModel.Text;
   //Asiganamos el metodo de cambio de valor de nuestra caja
   et.Changed += (object sender, EventArgs e) => {
    //el valor de la variable Text de nuestro contexto es asiganado con el nuevo valor de la caja de texto
    viewModel.Text = et.Text;
   };

   //Creamos nuestro boton
            UIButton btn = new UIButton(new RectangleF(0, 120, 320, 50));
   //Asignamos el texto del boton
            btn.SetTitle(viewModel.TimesString, UIControlState.Normal);
   //asignamos el evento de click en el boton
            btn.TouchUpInside += (s,e) => viewModel.ClickEvent.Execute(null);
   //Agregamos efecto de toque al boton
            btn.ShowsTouchWhenHighlighted = true;

   //Al igual que en la aplicacion de android
   //Asignamos el evento PropertyChanged de nuestra variable contexto el cual nos retornara la accion realiza para asi hacer responsiva nuestra UI
   viewModel.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => 
   {
    //Verificamos cual de las propiedades fue actualizada
                if (e.PropertyName == "Text")
                    lbl.Text = viewModel.Text;
                else if (e.PropertyName == "TimesString")
                    btn.SetTitle(viewModel.TimesString, UIControlState.Normal);
   };

   //Añadimos nuestra label a la vista principal
   View.Add (lbl);
   //Añadimos nuestro campo de texto a la vista principal
   View.Add (et);
   //Añadimos nuestro boton a la vista principal
            View.Add (btn);
  }
 }
}

A continuacion ya podremos probar nuestra aplicacion responsiva Xamarin.iOS utilizando el modelo MVVM



Codigo fuente en GitHub: https://github.com/AlejandroRuiz/Mono/tree/master/MvvmCrossXamarin

Desarrollando aplicaciones multiplataforma en 60 min con Xamarin aprovechando el modelo MVVM: http://channel9.msdn.com/Blogs/DevRadio/Microsoft-DevRadio-Developing-for-Windows-8-in-1-2-the-Time-60min-Challenge-Buidling-Cross-Platform-

Seria un post extremadamente largo el detallar el uso de cada una de las interfaces quize redondear todo y solo poner los puntos importantes de cada plataforma en caso de que tengan alguna duda en concreto con el post o alguna de las plataformas con gusto puedo resolverla mediante mi twitter @alejandroruizva o en mi correo elgoberlivel@gmail.com espero y esta informacion sea de su ayuda, hasta la proxima.



Comentarios

Entradas populares de este blog

Bluetooth Arduino + Xamarin.Android

Simple ListView with Xamarin.Forms

Xamarin.Forms: Get native image from ImageSource