Windows Phone – MVVM IV

En el anterior articulo vimos el hola mundo del MVVM con el framework  MVVM Light Toolkit, en este vamos a fijarnos en la clase Messenger que viene en este FrameWork. Esta clase usa un simple modelo publicador/suscriptor (similar al EventAggregator de PRISM, pero de uso más sencillo)  para la transmisión mensajes sin establecer fuertes dependencias entre los comunicados. Los que desean recibir mensajes usan el método Register, y los que desean enviar mensajes usan Send, de esta manera se pueden comunicar componentes pero de forma desacoplada sin que ninguno conozca la existencia del otro.

Para realizar el ejemplo nos vamos a basar en el anterior solo que vamos a añadir una ventana nueva que se invocara desde la ventana principal y que solo tendrá un texbox para que el usuario introduzca un nuevo elemento en el combobox. Os adjuntare el código para que lo estudiéis ya que va a ser un poco complicado explicarme.

Añadimos al proyecto una nueva vista denominada SecondWindow junto con su ViewModel, recordar, utilizar el template que nos proporciona MVVM Light Toolkit

 

image

 

En nuestro caso lo denominamos MvvmViewModelSecondWindow, siempre que creamos un nuevo ViewModel debemos de añadirlo a la clase ViewModelLocator del proyecto aprovechando el snippet que nos proporciona el framework o a mano, en este caso lo vamos a escribir a mano para que se afiancen conceptos.

Nos debería quedar de la siguiente forma

1 public class ViewModelLocator 2 { 3 private static MainViewModel _main; 4 private static MvvmViewModelSecondWindow _second; 5 6 7 /// <summary> 8 /// Initializes a new instance of the ViewModelLocator class. 9 /// </summary> 10 public ViewModelLocator() 11 { 12 ////if (ViewModelBase.IsInDesignModeStatic) 13 ////{ 14 //// // Create design time view models 15 ////} 16 ////else 17 ////{ 18 //// // Create run time view models 19 ////} 20 21 CreateMain(); 22 23 } 24 25 /// <summary> 26 /// Gets the Main property. 27 /// </summary> 28 public static MainViewModel MainStatic 29 { 30 get 31 { 32 if (_main == null) 33 { 34 CreateMain(); 35 } 36 37 return _main; 38 } 39 } 40 41 42 /// <summary> 43 /// Gets the Main property. 44 /// </summary> 45 public static MvvmViewModelSecondWindow SecondStatic 46 { 47 get 48 { 49 if (_second == null) 50 { 51 CreateSecond(); 52 } 53 54 return _second; 55 } 56 } 57 58 /// <summary> 59 /// Gets the Main property. 60 /// </summary> 61 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", 62 "CA1822:MarkMembersAsStatic", 63 Justification = "This non-static member is needed for data binding purposes.")] 64 public MainViewModel Main 65 { 66 get 67 { 68 return MainStatic; 69 } 70 } 71 72 /// <summary> 73 /// Gets the Main property. 74 /// </summary> 75 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", 76 "CA1822:MarkMembersAsStatic", 77 Justification = "This non-static member is needed for data binding purposes.")] 78 public MvvmViewModelSecondWindow Second 79 { 80 get 81 { 82 return SecondStatic; 83 } 84 } 85 86 /// <summary> 87 /// Provides a deterministic way to delete the Main property. 88 /// </summary> 89 public static void ClearMain() 90 { 91 _main.Cleanup(); 92 _main = null; 93 } 94 95 /// <summary> 96 /// Provides a deterministic way to delete the Main property. 97 /// </summary> 98 public static void ClearSecond() 99 { 100 _second.Cleanup(); 101 _second = null; 102 } 103 104 /// <summary> 105 /// Provides a deterministic way to create the Main property. 106 /// </summary> 107 public static void CreateMain() 108 { 109 if (_main == null) 110 { 111 _main = new MainViewModel(); 112 } 113 } 114 115 /// <summary> 116 /// Provides a deterministic way to create the Main property. 117 /// </summary> 118 public static void CreateSecond() 119 { 120 if (_second == null) 121 { 122 _second = new MvvmViewModelSecondWindow(); 123 } 124 } 125 126 /// <summary> 127 /// Cleans up all the resources. 128 /// </summary> 129 public static void Cleanup() 130 { 131 ClearMain(); 132 ClearSecond(); 133 } 134 }

Una vez introducido nuestro nuevo ViewModel en la clase ViewModelLocator, vamos a cambia la vista principal para añadirle un nuevo botón que nos permita abrir nuestra segunda vista

 

image

 

Para utilizar ese botón debemos crear un RelayCommand tal y como vimos en el anterior articulo

1 2 3 /// <summary> 4 /// Initializes a new instance of the MainViewModel class. 5 /// </summary> 6 public MainViewModel() 7 { 8 if (IsInDesignMode) 9 { 10 // Code runs in Blend --> create design time data. 11 } 12 else 13 { 14 _newItem = string.Empty; 15 Items = new ObservableCollection<ModelTest>(); 16 AddItemCommand = new RelayCommand(AddItem, CanAddNewItem); 17 AddItemNewWindowCommand = new RelayCommand(AddItemNewWindow); 18 } 19 } 20 21 private void AddItemNewWindow() 22 { 23 SecondWindow secondWindow = new SecondWindow(); 24 secondWindow.ShowDialog(); 25 26 } 27 ............

Y enlazamos el nuevo botón con su comando

 

1 <Button Command="{Binding AddItemNewWindowCommand}" Content="Insertar Nueva Ventana" Height="21" HorizontalAlignment="Left" Margin="115,67,0,0" Name="btnNewWindow" VerticalAlignment="Top" Width="138" />

Ahora deberíamos probar y comprobar que se nos abre la nueva ventana y en efecto así es. Nos falta ahora diseñar la nueva vista

 

 

image

 

Ahora nos metemos con su ViewModel, recordar que no debemos de tocar la vista (luego veremos que sera necesario), creamos el comando de Insertar Nuevo Item

Este ViewModel va a ser muy parecido al de MainWindow va a tener las propiedades NewItem, Items y el RelayCommand AddItemCommand lo que tenemos que hacer es que la clase Items contenga los items de la ventana MainWindow y que la devuelva con los items que haya insertado en la SecondWindow, pero todo ello sin que se conozcan las vistas. Para ello utilizaremos la clase Messenger.

En la clase MainWindowViewModel donde llamamos a la nueva vista utilizamos el metodo Send para enviar el mensaje con la variable Items . El método Send es tan sencillo como

 

void Send<T>(T message);

Así que podría ser 

Messenger.Default.Send(Items);

El problema es que este mensaje llegaria a todos los que registren un mensaje que les van a enviar de tipo ObbservableCollection<Model> que es el tipo de Items, para acotar a la viewmodel a la que queremos enviar utilizamos

void Send<TMessage, TTarget>(TMessage message);

En nuestro caso

1 private void AddItem() 2 { 3 ModelTest newItem = new ModelTest(); 4 newItem.Code = Items.Count == 0 ? 1 : Items[Items.Count - 1].Code + 1; 5 newItem.Description = NewItem; 6 7 Items.Add(newItem); 8 Messenger.Default.Send<ObservableCollection<ModelTest>, MvvmViewModelSecondWindow>(Items); 9 10 11 }

 

Nos queda ver como recibir ese mensaje, para ello primero lo registramos en la ViewModel MvvmViewModelSecondWindow en su constructor

 

1 public MvvmViewModelSecondWindow() 2 { 3 if (IsInDesignMode) 4 { 5 // Code runs in Blend --> create design time data. 6 } 7 else 8 { 9 _newItem = string.Empty; 10 11 AddItemCommand = new RelayCommand(AddItem); 12 Messenger.Default.Register<ObservableCollection<ModelTest>>( 13 this, 14 Lista => Items = Lista); 15 16 } 17 18 19 }

En el registro del mensaje indicamos su contenedor en este caso la propia clase y una acción a realizar con el parámetro que nos envía el mensaje, de esta manera ya tenemos la propiedad Items con los valores de la clase MainWindowViewModel. Para enviar el nuevo item no haremos nada ya que la propiedad se pasa por referencia entre las ViewModel con lo que el combo se actualiza automáticamente

Solo nos queda cerrar la Vista, esto no se puede hacer desde la ViewModel, para ello tendremos que codificar el code-behind de la vista poniendo el evento click en el botón de insertar ejecutando el método Close para que se cierre la vista

Código

Download File – MVVMMessenger

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *