Con la llegada del Visual Studio 2008 y el cada vez más popular framework WPF (Windows Presentation Foundation), el uso del patrón MVVM para el desarrollo de aplicaciones se ha hecho prácticamente indispensable. Actualmente ya existen varios frameworks MVVM, algunos de ellos son de código libre, como por ejemplo Cinch.
La máxima del patrón MVVM es la separación de las capas de presentación y negocio. Esto se consigue con el nuevo lenguaje declarativo llamado XAML que permite establecer el binding entre los elementos visuales y los elementos del dominio, en nuestro caso las propiedades de las clases. Así, imaginemos que queremos enlazar un atributo de la clase que represente el nombre del empleado con la caja de texto que al iniciar la aplicación se muestre el nombre. En Windows Forms deberíamos crear el manejador de evento de carga del formulario, declarar la instancia de nuestra clase Empleado, e inicializar el valor de la propiedad Text de la caja de texto. Sin embargo en WPF, basta con escribir una instrucción en XAML que enlace nuestra caja de texto con el atributo. Lo mejor de todo es que al modificar el valor del texto, también se actualizará el valor del atributo que representa el nombre del empleado, y al revés.
Las siglas MVVM vienen de Model-View-ViewModel, donde como su propio nombre indica, Model representa el dominio de nuestro sistema, View los elementos que componen la interfaz y ViewModel se encargará de comunicar las vistas con el modelo.
La vista nunca interactuará directamente con el modelo, sino a través de los ViewModels.
Los desarrolladores familiarizados con ASP.NET pueden establecer un símil con WPF. Las páginas en ASP.NET disponen del código HTML y del code behind escrito en C# o Visual Basic. De manera similar, en WPF tenemos los archivos .xaml, y detrás de cada vista código en nuestro lenguaje de programación favorito como VB o C#. Con MVVM conseguiremos que la cantidad de código en code behind sea la mínima posible.
Después de esta breve introducción teórica veamos como llevar a cabo la implementación básica del patrón MVVM en nuestra aplicación. Debido que muchos de los ViewModels necesitan la misma información, empezaremos creando la clase base ViewModelBase que heredarán nuestros ViewModels. Para organizarnos mejor, es buena práctica crear dentro de nuestro proyecto tres carpetas:
- ViewModels donde iremos alojando nuestro ViewModels
- Models para almacenar nuestros modelos
- Views para las vistas
public abstract class ViewModelBase : INotifyPropertyChanged { /// /// Evento que se ejecuta cuando se cambia la propiedad del componente. /// public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(String sData) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(sData)); } } }
Como vemos debe implementar la interfaz INotifyPropertyChanged, concretamente el miembro NotifyPropertyChanged, que notificará al sistema de binding de WPF cuando la propiedad en el ViewModel disponga de nuevo valor, para actualizar en consecuencia todos los controles enlazados.
Para que WPF se de cuenta de que propiedad ha cambiado, al disparar el evento PropertyChanged, hemos de pasarle en el constructor de la clase PropertyChangedEventArgs el nombre completo de la propiedad.
A partir de ahora podemos empezar a definir nuestros ViewModels. Para el caso de estudio vamos a definir uno trivial que invoque los métodos del modelo añadiendo la cadena que se le pasa a la lista, eliminándola o obteniendo todas las cadenas disponibles.
public class ExampleViewModel : ViewModelBase { private ExampleModel exampleModel = new ExampleModel(); private ObservableCollection lDummyStrings = new ObservableCollection (); private void doDummyAdd(String sDummy) { exampleModel.addDummy(sDummy); } private void doDummyDel(String sDummy) { exampleModel.delDummy(sDummy); } private ObservableCollection getDummies() { return exampleModel.getDummies(); } }
En una aplicación del mundo real el modelo se compondría de operaciones mucho más complejas, como el acceso a bases de datos. Anteriormente habíamos mencionado que el objetivo del MVVM es desacoplar el código correspondiente a la vista, pero si queremos saber cuándo el usuario aprieta un determinado botón o elige una valor de la lista, deberíamos instalar manejadores de eventos fuertemente acoplados a la vista. Por suerte, WPF incorpora un sistema de comandos mediante los cuales podemos llevar la lógica correspondiente de los manejadores de eventos a los ViewModels.
En primer lugar implementamos el patrón del comando genérico. Como vemos contiene dos delegados que podemos interpretar como, si la condición (el delegado CanExecuteDelegate) del comando se cumple entonces podemos ejecutamos la acción (ExecuteDelegate).
/// /// El comando génerico para ejecutar los métodos /// de la interfaz ICommand. /// public class GenericCommand : ICommand { public Predicate public Action /// /// Indica si el comando se puede ejecutar. /// public bool CanExecute(Object param) { if (CanExecuteDelegate != null) return CanExecuteDelegate(param); return true; } /// /// Se dispara cuando cambia la condición de ejecución del /// comando. /// public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove{ CommandManager.RequerySuggested -= value; } } /// /// Ejecutará el método del comando. /// public void Execute(Object param) { if (ExecuteDelegate != null) ExecuteDelegate(param); } }
Declaramos el comando que nos permitirá insertar la cadena que se le pase como parámetro en la colección:
private ICommand addDummyStringCommand;
Inicializamos los delegados del comando. Para ello vamos a usar una nueva estructura de .NET 3.0 denominada expresiones lambda, que de manera mucho más limpia nos permiten hacer lo que en la versión anterior del framework se hacía mediante delegados anónimos:
private void doInitCommand() { this.addDummyStringCommand = new GenericCommand(){ CanExecuteDelegate = c => c != null, ExecuteDelegate = c => doDummyAdd((String) c )}; } public ICommand AddDummyStringCommand{ get {return this.addDummyStringCommand;} set{ this.addDummyStringCommand = value; NotifyPropertyChanged("AddDummyStringCommand"); } }
Solamente cuando el parámetro sea distinto de null se podrá ejecutar el comando, es decir, el botón se habilitará.
Ahora veamos como enlazar nuestro comando con la vista desde el código XAML. Debemos inicializar la propiedad DataContext del elemento que participa en el data binding, en este caso la vista. Para ello en el constructor de la misma hacemos lo siguiente:
public partial class ExampleView { private ExampleViewModel exampleViewModel = new ExampleViewModel(); public ExampleView(){ InitializeComponent(); this.DataContext = exampleViewModel; } }
Con esto hemos establecido la conexión entre la UI de la aplicación y nuestro ViewModel. Ahora seremos capaces desde el código XAML de referenciar las propiedades de nuestro ViewModel.
<Button Name="cmdAddDummyString" Command="{Binding Path=AddDummyStringCommand}" CommandParameter="{Binding ElementName=txtDummyString, Path=Text}"></Button>Aquí la propiedad Command del botón la hemos enlazado con el comando de nuestro ViewModel y el parámetro será la cadena que se escribirá en la caja de texto. Todo queda limpio, sin necesidad de invocar las operaciones de la lógica de negocio desde los manejadores de eventos. Además, de esta forma estamos reutilizando el código; si quisiéramos incorporar a nuestra aplicación la misma funcionalidad pero desde, por ejemplo, el menú, simplemente asignaríamos el mismo comando al ítem del menú
Imaginad ahora que queremos listar todas la cadenas que se vayan añadiendo o eliminando de la colección, en un Listbox. Si volvemos a la definición de nuestro ViewModel, el atributo lDummyStrings de tipo ObservableCollection se comporta como una lista normal, excepto que podemos ser notificados cuando se eliminan o añaden elementos a la misma. Suponiendo que en el ViewModel hemos definido la propiedad para devolver la lista, veamos como enlazarla con el Listbox:
<ListBox Width="Auto" Height="Auto" ItemsSource="{Binding Path=BindableDummyStringCollection}"></Button>Lo que en Windows Forms resultaría un proceso engorroso, en WPF mediante un par de líneas en XAML hemos enlazado el Listbox con la colección, y además a medida que se va modificando la colección el Listbox se actualiza automáticamente.
Hemos aprendido como llevar a cabo la implantación minimalista del patrón MVVM en nuestra aplicación mediante un ejemplo trivial, pero que sirve como el guía para desarrollar aplicaciones complejas. Actualmente, frameworks como Cinch o Calcium ofrecen muchas características que nos pueden ayudar a la hora de implementar el MVVM.
Sin duda alguna, se está convirtiendo en de facto para el desarrollo de aplicaciones WPF.












octubre 26th, 2009 - 13:26
Muy buen articulo. XAML realmente es muy parecido al “Declarative Binding” de .Net por lo que a mi me parece, no?
Tengo una preguntilla. Cómo se comporta XAML con un DropDown si haces el binding a la propiedad SelectedValue? Que pasa cuando el elemento “ligado” no se encuentra en la lista de valores del DropDown?
Saludos!
octubre 26th, 2009 - 15:20
Si que es parecido al declarative binding de ASP.NET que mencionas. Sin embargo las posibilidades de XAML son mucho mas amplias cuando entran en juego las plantillas de datos, plantillas de controles, tiene su propio sistema de animación. etc.
Respecto a tu pregunta, si enlazas la propiedad ItemsSource con por ejemplo una lista, y luego estableces la propiedad SelectedValue, pero ese valor no se encuentra en la lista, simplemente no se mostrará en el área editable de ComboBox.
Saludos.
noviembre 6th, 2009 - 18:10
Genial tio, como siempre. Me ha gustado la explicacion y has conseguido abrir un debate en mi empresa.
Sigue asi y a ver cuando haces el siguiente articulo pero esta vez implementando MVVM con Mediator.
Un saludo!