Xamarin.forms y MVVM

¡Hola a todos!

Debido al webinar que he impartido recientemente, he decidido crear un post sobre el tema para todo aquel que le pueda interesar repasar MVVM utilizado en Xamarin.Forms. 

Además, al final del post encontrarás un enlace en el que podrás descargarte la presentación completa. 

Introducción

La mayoría de las empresas dedicadas al desarrollo de aplicaciones de escritorio, Xamarin, Windows 10, etc. han adoptado el patrón MVVM como base del desarrollo de sus aplicaciones. Por eso, es muy importante adquirir los conceptos básicos del mismo:

¿Qué es MVVM – Model View ViewModel? 

Se trata de un patrón de desarrollo que nos permite separar o desacoplar la interfaz de usuario del resto del código. 

Podemos dividir nuestra aplicación en tres capas: 



Flujo Mvvm

Model

Representa la capa de datos y la logica de negocio de nuestra app. También es denominado como objeto del dominio.

View

Interfaz de usuario o Vistas de nuestra app. En Xamarin.Forms podemos crear interfaces de usuario con XAML o con C#. En nuestro caso, utilizaremos XAML para diferenciar bien la interfaz de usuario de la lógica de presentación (ViewModel)

ViewModel

Se trata de la lógica de presentación de nuestra vista. Realiza la función de intermediario entre el Modelo y la Vista. El ViewModel contiene el estado de la vista y se comunica con ella a través de Data Binding, Commands y Notificaciones gracias a la interfaz:

INotifyPropertyChanged

 



Flujo Mvvm 2

Esta separación nos da varias ventajas:

  • Separar el desarrollo de la interfaz de usuario del resto del código. Es decir, podemos tener un equipo de diseño trabajando en la interfaz de usuario y a los programadores haciendo el resto de la aplicación.
  • La lógica de la presentación puede ser testeada con test unitarios al estar desacoplada de nuestras vistas.
  • Muy útil cuando estamos desarrollando aplicaciones multiplataforma, ya que tanto la lógica de negocio como la lógica de presentación es común a todas las plataformas.

Bindings

Como hemos dicho, la forma de comunicación entre el ViewModel y la Vista es a través de Data BInding y Commands. Esta comunicación es posible gracias a la interfaz: 

INotifyPropertyChanged

Veamos un ejemplo de la implementación de esta interfaz:


public class MyViewModel : INotifyPropertyChanged
{
    public MyViewModel ()
    {
    }
    
    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, 
                new PropertyChangedEventArgs(propertyName));
    }

    private string _Title;

    public string Title {
        get {
            return _Title;
        }
        set {
            _Title = value;
            RaisePropertyChanged();
        }
    }
}

Como podemos comprobar, lo que realmente hace esta interfaz, es lanzar un evento cada vez que una propiedad cambia. Nuestra vista estará suscrita a estos eventos de cambio y eso es lo que llamamos Data Binding o Binding. 

Un Binding relaciona dos propiedades entre sí de forma que se mantengan sincronizadas. Existen dos tipos de Bindings. Para explicarlos y entenderlos mejor, imaginemos que queremos «Bindear» una propiedad string Title de nuestro ViewModel a la propiedad  «Text» de un control Entry de nuestra vista:

One Way Binding

Los cambios que realicemos en la Propiedad «Title» de nuestro ViewModel se propagarán a la propiedad «Text» del control «Entry», pero no se actualizará «Title» en el caso de que editemos la caja de texto:


// Código en nuestro ViewModel:
private string _Title;
public string Title {
    get {
        return _Title;
    }
    set {
        _Title = value;
        RaisePropertyChanged();
    }
}
// Binding en nuestro archivo XMAL
<Entry Text="{Binding Title}" />

Two Way Binding

Las dos propiedades enlazadas tienen la capacidad de propagar los cambios. Es decir, si cambiamos en la propiedad de nuestro ViewModel, se informará a la vista del cambio, y si editamos la caja del texto mandará el valor que escribamos a la propiedad «Title».


// Código en nuestro ViewModel:
private string _Title;
public string Title {
    get {
        return _Title;
    }
    set {
        _Title = value;
        RaisePropertyChanged();
    }
}
// Binding en nuestro archivo XMAL
<Entry Text="{Binding Title, Mode=TwoWay}" />

oneway-two-way.jpg

 

Commands

Ya que queremos trasladar toda la lógica de la aplicación a nuestros ViewModels, debemos incluso trasladar Eventos como por ejemplo, sobre el click de un botón. Esta abstracción de eventos sobre controles en la vista es posible gracias a la interfaz:

ICommand

La forma  de enlazar un «Command» con el control de una vista es también a través de Binding:

// Definición de un Command en nuestro ViewModel
private ICommand _SearchByName;

public ICommand SearchByNameCommand {
    get {
        return _SearchByName ?? (_SearchByName = new Command (
            async () => await ExecuteSearchByNameCommand (),
            CanExecuteSearchByNameCommand)); 
    }
}

private async Task ExecuteSearchByNameCommand ()
{
    await LoadData (SearchText);
}

private bool CanExecuteSearchByNameCommand()
{
    return SearchText.Lenght > 0;
}
// Command binding en nuestro archivo XMAL
<SearchBar Text="{Binding SearchText}" 
SearchCommand="{Binding SearchByNameCommand}">

Conceptos a revisar de MVVM para Xamarin.Forms: 

Estructura básica de un proyecto MVVM-Xamarin.Forms

Xamarin.Forms y Mvvm · Blog Ramon Esteban


Proyecto básico Xamarin.Forms

 

En cuestión de proyectos, podemos distinguir los siguientes:

  • Core (XamFormsMarvel)
  • Android (XamFormsMarvel.Droid)
  • iOS (XamFormsMarvel.iOS)
  • UWP (XamFormsMarvel.UWP)
  • UITest (XamFormsMarvel.UITests) – Este es un proyecto opcional

 

Analicemos ahora en concreto el proyecto Core. Estas son las carpetas a las que tenemos que dar importancia:

  • Models – Nuestros modelos de datos
  • ViewModels – Tendremos un ViewModel por Vista
  • Views – Archivos XAML con la interfaz de usuario

BaseViewModel

Una de las primeras acciones a tomar a la hora de empezar con el patrón MVVM es crear nuestro clase base ViewModel. Esta clase será la encargada de implementar la interfaz: «INotifyPropertyChanged

Todos nuestros ViewModels heredarán de esta clase base.

Aunque la creación de una clase base no es obligatoria, sí que es recomendable, ya que así evitamos implementar 

INotifyPropertyChanged en todos nuestros ViewModels


public class BaseViewModel : INotifyPropertyChanged
{
    public BaseViewModel () { }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, 
                new PropertyChangedEventArgs(propertyName));
    }
    private bool _IsBusy;
    public bool IsBusy {
        get {
            return _IsBusy;
        }
        set {
            _IsBusy = value;
            RaisePropertyChanged();
        }
    }
    private string _Title;
    public string Title {
        get {
            return _Title;
        }
        set {
            _Title = value;
            RaisePropertyChanged();
        }
    }
}

Converters:

Muchas veces el valor que recibimos de nuestro ViewModel no es suficiente para nuestra Vista y necesita de un tratamiento adicional. En este tipo de situaciones haremos uso de los Converters, que son clases auxiliares que efectúan una determinada acción sobre un Binding y que complementan la interfaz:


IValueConverter.

Veamos un ejemplo de implementación de un Converter y cómo se realiza el Binding en nuestra «Vista»:


public class DescriptionToImageValueConverter : IValueConverter
{
    public DescriptionToImageValueConverter()
    {
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string strValue = value?.ToString();

        if (string.IsNullOrEmpty(strValue))
        {
            return ImageSource.FromFile("wrong");
        }
        else
        {
            return ImageSource.FromFile("ok");
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

 

En el caso de nuestra Vista XMAL, los converters deben de ser declarados como recursos estáticos de nuestro ContentView o ContentPage, y después, utilizarlos en el Binding:



    
        <local:DescriptionToImageValueConverter x:Key="okWrongConverter">
    


<Image Source="{Binding Description, 
Converter={StaticResource okWrongConverter}}">

Conclusión

Hemos revisado los conceptos básicos de MVVM y cómo aplicarlos en una aplicación Xamarin.Forms. Os dejo el enlace de Github con el código en el que está basado el post. ¡Echad un vistazo para profundizar un poco más en los conceptos de MVVM!

Frameworks existentes de MVVM que podemos usar con Forms: 

Últimos consejos y opiniones personales sobre MVVM

Aunque MVVM es un patrón de desarrollo muy ventajoso, hay que tener cuidado en ciertas cosas. Lo que planteo aquí son mis propias conclusiones y opiniones después de haber trabajado con MVVM durante años y en proyectos de cierta complejidad.

Relación entre Vista y ViewModel

Es muy importante dejar claro que nuestro ViewModel no tenga conocimiento alguno de la Vista al que va a ser bindeado. Si tenemos claro este concepto durante el desarrollo, el mismo ViewModel puede servirnos para la vista del iPad, la de iPhone, la de Mac, la de UWP, etc.

El contrario no aplica, la Vista tiene la libertad de estar altamente acoplada a un ViewModel en concreto. 

Existe una tercera opción de Binding 

La utilización de Mvvm muchas veces, nos lleva al error de crear todas las propiedades de nuestro ViewModel ejecutando el 

PropertyChanged y lancen un evento informando a la vista. En muchas ocasiones, nos encontraremos que no necesitamos que se propaguen cambios hacia la vista, sino que se tratan de propiedades que son únicamente  «readonly»

Por ejemplo, imaginaos el título de una Vista, el cual es solo informativo y no cambia, y por tanto no necesita cambios de ningún tipo. Lo trataríamos de la siguiente forma:


// Código en nuestro ViewModel:
public string Title { get; private set; }
// Binding en nuestro archivo XMAL
<Entry Text="Binding Title"/>

Reducir el número de Bindings o eventos entre propiedades de nuestro ViewModel y Vista, es bueno para el rendimiento de la app que estamos implementando.

Control del estado de nuestra vista: 

Hay que procurar que el estado de nuestra vista siempre sea, en la medida de lo posible, a través de «Commands«, evitar el efecto rebote que puede provocar que el en «set» de una propiedad se actualice en otra propiedad y así sucesivamente. Esto hace que perdamos el control del estado de la vista.

Messenger

Existe otra forma de comunicación entre la Vista y los ViewModels que es usando un patrón MessageBus. En Xamarin.Forms es «Messenger«. Aunque algunas veces no tenemos más remedio que hacerlo, debemos procurar evitarlo.

Code Behind or note Code Behind

La mayoría de los programadores que usan MVVM no les gusta nada tener código en la base parcial de nuestro XAML (code-behind). Aunque esto es posible llevarlo a cabo, a través de «Triggers» o «Behaviours», en mi opinión da lugar a un XAML que es difícil de mantener y entender de un primer vistazo. Yo personalmente, prefiero utilizar el code-behind e implementar acciones específicas de la Vista, como animaciones, posición de scroll, etc. Esto da lugar a un XAML con prácticamente Binding y estilos más mantenible y conciso en un primer vistazo.

 

¡Esto es todo por hoy!

¡Si queréis saber más, podéis rellenar el formulario y descargaros la presentación del webinar impartido!

ppt-download-xamarin.png