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.
