El ViewModel es la parte del patrón MVVM dónde vamos a establecer las distintas propiedades públicas que se van a enlazar a la Vista, así como los distintos comandos de la lógica de presentación.
Depende de quién sea el desarrollador, hay varias convenciones a la hora de nombrar los ficheros del ViewModel. A mi personalmente me gusta crear un namespace llamado ViewModel, pero a otros les gusta llamar a las clases con el sufijo (o prefijo) VM o ViewModel.
Agregamos tres clases nuevas al proyecto, Search, Weather y MainWindow, que van a ser las que utilizaremos para el ViewModel.
INotifyPropertyChanged
Esta interfaz, es fundamental en el patrón MVVM, nos permite actualizar los elementos de la vista que están enlazados a propiedades en la vista-modelo, para ello basta con implementarla en la ViewModel y al modificarse una propiedad lanzamos el evento del cambio. Por ejemplo algo así:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; namespace WeatherApp.ViewModel { public class MainWindow : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } private String _sample; public String Sample { get { return _sample; } set { _sample = value; OnPropertyChanged("Sample"); } } } }
Al realizar cualquier cambio en la propiedad «Sample» automáticamente se reproduce el cambio en la propiedad o propiedades que la tengan enlazada en la Vista, bueno y siendo rigurosos también dependiendo del modo en que esté enlazado, pero en principio asumimos un modo de enlace «TwoWay».
ICommand
Esta interfaz, dispone de tres miembros (un método, un booleano y un manejador de evento), y es el mecanismo a través del cual los distintos controles del XAML van a realizar operaciones, la ventaja fundamental de esta interfaz frente a los eventos, es que no deja de ser una propiedad en la vista-modelo, y por tanto podremos enlazarlos con la vista y reutilizarlos.
using System; using System.Windows.Input; namespace WeatherApp.ViewModel { public class Weather : ICommand { public void Execute(object parameter) { throw new NotImplementedException(); } public bool CanExecute(object parameter) { throw new NotImplementedException(); } public event EventHandler CanExecuteChanged; } }
El método es la lógica de la acción, la función booleana es un «flag» que permite ejecutar el comando, y bueno el manejador controla la ejecución del comando.
Implementando las interfaces
Pero…, si tengo que implementar todo este código por cada acción de la aplicación, o para establecer la notificación de las propiedades de cada clase, ¿dónde está el beneficio?. Pues evidentemente esto para una aplicación sencilla con pocos comandos y con pocas vistas-modelos, es algo asequible, pero para aplicaciones más complejas, conviene elaborar una clase que delegue la implementación del «ICommand», y a la que podamos enviar la ejecución de la acción y sus parámetros.
using System; using System.Diagnostics; using System.Windows.Input; namespace WeatherApp.Common { public class RelayCommand : ICommand { private readonly Action<object> _execute; private readonly Predicate<object> _canExecute; public RelayCommand(Action<object> execute) : this(execute, null) { } public RelayCommand(Action<object> execute, Predicate<object> canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } _execute = execute; _canExecute = canExecute; } [DebuggerStepThrough] public bool CanExecute(object parameters) { return _canExecute == null || _canExecute(parameters); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameters) { _execute(parameters); } } }
Para la notificación de propiedades, generaríamos una clase abstracta, que implementase en un método público, «INotifyPropertyChanged», y después cada clase del ViewModel implementaría esta clase abstracta, llamando únicamente al método que lanza la implementación.
using System.ComponentModel; namespace WeatherApp.Common { public abstract class ObservableObject : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } }
Después de implementar todo esto, en el siguiente post, toca empezar a agregar las propiedades al ViewModel, establecer la notificación de los cambios para que cuando se produzcan se actualicen inmediatamente en la vista, y enlazarlas al marcado XAML mediante el contexto de datos y los Binding.