El Patrón Model - View - ViewModel ( MVVM )

El patron Model - View - ViewModel fue concebido por John Gossman allá por el año 2005 en un post de su blob titulado "Introduction to Model/View/ViewModel pattern for building WPF apps"(http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx), siendo este una adaptación del patrón Presentation Model (http://martinfowler.com/eaaDev/PresentationModel.html) propuesto por Martin Fowlers para tecnologías .NET como XAML(como se conoce ahora al conjunto de herramientas para desarrollar en Windows 8), WPF y Silverlight.

Este patrón junto a otros mas conocidos como MVC o MVP tiene por objetivo simplificar las tareas de desarrollo y mantenimiento del software escrito con estos a través de la división de ocupaciones, por lo cual alguien que ya haya trabajado previamente con alguno de los patrones previamente mencionados, le parecerá bastante familiar MVVM.

Model
El modelo, dentro de MVVM es el encargado de representar el modelo del negocio, proveyendo de esta manera la base necesaria para la manipulación de los datos de la aplicación, además parte del modelo se lo puede usar como clases POCO (Plain Old CLR Objects) para poder usarlas con Entity Framework Code First o algún otro ORM. Cabe resaltar que en el modelo, no debería de existir ninguna lógica de negocio o código que afecte a como se visualizan sus datos en pantalla.

View
La vista es la parte encargada de la parte visual de nuestra aplicación, no teniéndose que ocupar en ningún momento en el manejo de datos. En MVVM la vista tiene un rol activo, esto significa que en algún momento la vista recibirá o manejara algún evento (Clic en un botón, alguna tecla presionada, etc.) y tendrá que comunicarse con el modelo, para poder cumplir el requerimiento.

ViewModel
El ViewModel (modelo de vista en español) es el encargado de ser la capa intermedia entre el modelo y la vista, procesando todas las peticiones que tenga la vista hacia el modelo, además de tener que ocuparse de manejar las reglas del negocio, la comunicación con aplicaciones externas o consumir datos desde alguna fuente (Bases de Datos, Web Services, Sensores, etc.). Para la comunicación entre el View y el ViewModel, utilizamos los enlaces de datos que nos permiten tener un código bastante limpio (Bindings http://msdn.microsoft.com/en-us/library/ms752347.aspx)


Observaciones
Trabajando con este patrón y migrando cientos de líneas de código Espagueti al patrón, he visto algunas practicas o concejos que pueden ser de bastante utilidad al menos al momento de comenzar a usar el patrón.

- El Code-Behind no tiene que existir: He escuchado bastante de eso por todo ello, y le cuento que es un tanto falso, para entender mejor como funciona el code-behind dentro del patrón MVVM, hay que pensarlo como si fuere el patrón MVC, donde la vista este escrita en HTML y el JavaScript que maneja todo lo referente a la vista(cambiar un color, redimensionar un control, etc.); ciertamente el Code-Behind hay que usarlo con cautela, pero a la vez prescindir por completo de el puede llevarnos demasiado tiempo, por regla, si algo que nos puede tomar un par de minutos con Code-Behind, nos toma horas sin el, entonces estamos yendo por mal camino.

- La vista debe ser 100% desacoplable de ViewModel: Esto al igual que el punto anterior hay que pensar primero en la dificultad que realizar esto conlleva, por que ciertamente no es a función principal del patrón, si es un aditamento, si es que es posible implementarlo en un lapso de tiempo corto y sin tener que crear una mayor estructura para que se lleve a cabo.

- No es necesario usar un Framework para MVVM: Esto es completamente cierto, ya que usando un par de clases escritas por nosotros o por terceros podemos tener nuestro proyecto con MVVM al 100%.

- MVVM es solo para aplicaciones grandes: Muy falso, MVVM funciona perfectamente bien para proyectos grandes y pequeños, inclusive por las características de WPF, Silverlight o XAML el desarrollo de toda aplicación es mas simple usando el patrón. En todo caso, al igual que el punto 2 el tiempo y la complejidad nos dirán si estamos aplicando de manera correcta el patrón, ya que perder horas montando la infraestructura para un proyecto que no requiere mas de unos minutos, no es lo correcto.

Ejemplo
Bueno, para poder entender el patrón que mejor que un ejemplo simple que nos mostrara como hay que iniciar un proyecto con MVVM, el ejemplo consistirá en una tabla que muestre una agenda de contactos.
Nuestro modelo será como sigue:
 
class Contact
{
        public string Name { get; set; }
        public string Email { get; set; }
        public string Address { get; set; }
        public string PhoneNumber { get; set; }
        public string Twitter { get; set; }
        public bool onGPlus { get; set; }
}
El siguiente paso sera definir el View-Model, para lo cual primero obtendremos la clase delegate command que nos facilita el trabajo con la reduccion de codigo repetitivo. La clase se encuentra en la siguiente direccion: http://www.wpftutorial.net/DelegateCommand.html
Ya teniendo creada la clase Delegate Command, creamos otra clase llamada ContactViewModel que sera como sigue:
 
class ContactViewModel : INotifyPropertyChanged
{
 //Definicion de la Lista de Contactos
 private ObservableCollection<Contact> _Contacts;
 public ObservableCollection<Contact> Contacts
 {
  get
  {
    return _Contacts;
  }
  set
  {
    _Contacts = value;
    NotifyPropertyChanged("Contacts");
  }
 }


 //Construtor de la clase donde ademas cargamos datos de prueba
 public ContactViewModel()
 {
        _Contacts = new ObservableCollection<Contact>();
        _Contacts.Add( new Contact() { Name = "Andiria", Address = "Calle Z", Email = "andiria100@hotmail.com", PhoneNumber = "(591) 764215888", Twitter = "@sisandy", onGPlus=true });
        _Contacts.Add(new Contact() { Name = "Fernanda", Address = "Calle X", Email = "fernanda100@hotmail.com", PhoneNumber = "(591) 764215889", Twitter = "", onGPlus = false });
        _Contacts.Add(new Contact() { Name = "Nataly", Address = "Calle Z", Email = "naty100@hotmail.com", PhoneNumber = "(591) 764215810", Twitter = "", onGPlus = false });
        _Contacts.Add(new Contact() { Name = "Francisca", Address = "Calle W", Email = "fran100@hotmail.com", PhoneNumber = "(591) 764215811", Twitter = "", onGPlus = true });
  _Contacts.Add(new Contact() { Name = "Fabiana", Address = "Calle W", Email = "fab100@hotmail.com", PhoneNumber = "(591) 764215811", Twitter = "", onGPlus = true });
  AddContactCommand = new DelegateCommand(AddContact_Execute, AddContact_CanExecute);
 }

//Implementacion de la Interface INotifyPropertyChange
#region PropertyChanged
 public event PropertyChangedEventHandler PropertyChanged;
 private void NotifyPropertyChanged(string info)
 {
  PropertyChanged(this, new PropertyChangedEventArgs(info));
 }
#endregion

 //Definicion del comando encargada de agregar datos a la colección de elementos
#region Add Contact

 public DelegateCommand AddContactCommand
 {
  get;
  private set;
 }
 void AddContact_Execute(object parameters)
 {
  Contacts.Add(new Contact() { Name = "Test1", Address = "X", Email = "X", PhoneNumber = "X", Twitter = "X" });
 }
 bool AddContact_CanExecute(object parameters)
 {
  return true;
 }
#endregion
}

Como vemos en el codigo, se tienen 3 secciones, la primera seccion encargada de definir las propiedades del View-Model y de cargar datos de prueba; la segunda parte encargada de realizar la implementacion de la Interface INotifyPropertyChanged, que es la encargada de notificar a los clientes (para este caso delo clientes de que hagan Binding a cierta propiedad) que ha habido un cambio en su valor de cierta propiedad.
El ultimo paso sera crear nuestra interface, donde pondremos un DataGrid, un listbox, un boton para agregar datos y por ultimo 2 Textbox para editar los datos, el codigo XAML seria como sigue:
 
 <Window x:Class="TestMVVM.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="MainWindow" Height="420" Width="525">
 <Grid>
 <DataGrid AutoGenerateColumns="True" Height="153" HorizontalAlignment="Left" Margin="12,12,0,0" Name="dataGridContacts" VerticalAlignment="Top" Width="479" ItemsSource="{Binding Contacts}" />
 <Button Content="Add Contact" Height="23" HorizontalAlignment="Left" Margin="231,206,0,0" Name="btnChangeName" VerticalAlignment="Top" Width="112" Command="{Binding AddContactCommand}" CommandParameter="{Binding ElementName=dataGridContacts, Path=SelectedItem}" />
 <Label Content="Name: " Height="28" HorizontalAlignment="Left" Margin="34,204,0,0" Name="lblName" VerticalAlignment="Top" />
 <TextBox Height="23" HorizontalAlignment="Right" Margin="0,206,287,0" Name="txtName" VerticalAlignment="Top" Width="120" Text="{Binding ElementName=dataGridContacts, Path=SelectedItem.Name}" />
 <Label Content="Address: " Height="28" HorizontalAlignment="Left" Margin="34,243,0,0" Name="lblAddress" VerticalAlignment="Top" />
 <TextBox Height="23" HorizontalAlignment="Right" Margin="0,248,286,0" Name="txtAddress" VerticalAlignment="Top" Width="120" Text="{Binding ElementName=dataGridContacts, Path=SelectedItem.Address}" />
 <ListBox Height="126" HorizontalAlignment="Left" Margin="231,243,0,0" Name="TestListBox" VerticalAlignment="Top" Width="121" ItemsSource="{Binding Contacts}">
 <ListBox.ItemTemplate>
 <DataTemplate>
 <CheckBox Name="CheckBoxZone" Content="{Binding Name}" IsChecked="{Binding onGPlus}" Tag="{Binding MyGirlFriend}" Margin="0,5,0,0"/>
 DataTemplate>
 ListBox.ItemTemplate>
 ListBox>
 Grid>
 Window>
Notese que en la mayoria de propiedades de cargado de datos (DataSource) de los controles hemos usado Bindings a la colección de contactos que habiamos definido en nuestro View Model, de igual manera la propiedad command de nuestro boton apunta al Command que se definio en el View Model.
El ultimo paso para que nuestra aplicación funcione, consiste en enlazar el View-Model con la Vista, para hacer eso nos iremos al Code-Behind de la vista y lo modificaremos para que se vea de la siguiente manera:
 
public partial class MainWindow : Window
{
 ContactViewModel contactViewModel;
    public MainWindow()
    {
        InitializeComponent();
        contactViewModel = new ContactViewModel();
        this.DataContext = contactViewModel;
    }
}
Como se puede apreciar simplemente estamos creando el View-Model y luego lo asignamos a la propiedad DataContext de la Clase.
Si ejecutamos la aplicación deberia de verse asi:
Suerte!.

Comentarios

  1. Increíble introducción al patrón MVVM...

    ResponderEliminar
  2. El ejemplo mas claro que he encontrado

    Se agradece, sigue asi !

    ResponderEliminar
  3. Excelente explicación y practica; favor de continuar con los ejemplos.

    ResponderEliminar
  4. Al separar Model, ViewModel y View en proyectos independientes, donde el ViewModel tenga referencia a Modelo y View tenga referencia solo de ViewModel, las cosas son diferentes.

    en tu caso ViewModel requiere referencia del Model, quedando fuera del patrón MVVM.

    ResponderEliminar
  5. No comprendo muy bien la observacion, por favor podrias explicarla un poco mas para poder ver de corregir el articulo si es necesario

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

Conectarse a un Web Service en PHP con WPF

Ejecutar comandos en el CMD de Windows desde C#

Usar Log4Net en un proyecto ASP.NET MVC