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.
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)
Ejemplo
Suerte!.
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
View
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:
Muy bueno, gracias!!!
ResponderEliminarMuy buena explicacion!!
ResponderEliminarIncreíble introducción al patrón MVVM...
ResponderEliminarEl ejemplo mas claro que he encontrado
ResponderEliminarSe agradece, sigue asi !
Excelente explicación y practica; favor de continuar con los ejemplos.
ResponderEliminarperfecta explicación. Gracias!
ResponderEliminarAl 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.
ResponderEliminaren tu caso ViewModel requiere referencia del Model, quedando fuera del patrón MVVM.
No comprendo muy bien la observacion, por favor podrias explicarla un poco mas para poder ver de corregir el articulo si es necesario
ResponderEliminarEs correcto !!!
ResponderEliminar