Respondiendo a la barra de herramientas


Para hacer que la vista refleje responda a las acciones realizadas la hacer click en la barra de herramientashe utilizado el CompositeCommand de CompositeWPF, para ello he definido una clase con un comando por cada uno de los botones de la barra de herramientas

   1: public static class GeneralCommands
   2: {
   3:     #region · Static Commands ·
   4:  
   5:     public static CompositeCommand AddNewCommand    = new CompositeCommand(true);
   6:     public static CompositeCommand EditCommand      = new CompositeCommand(true);
   7:     public static CompositeCommand DeleteCommand    = new CompositeCommand(true);
   8:     public static CompositeCommand SaveCommand      = new CompositeCommand(true);
   9:     public static CompositeCommand SaveAllCommand   = new CompositeCommand(true);
  10:     public static CompositeCommand DiscardCommand   = new CompositeCommand(true);
  11:     public static CompositeCommand RefreshCommand   = new CompositeCommand(true);
  12:     public static CompositeCommand SearchCommand    = new CompositeCommand(true);
  13:     public static CompositeCommand PrintCommand     = new CompositeCommand(true);
  14:     public static CompositeCommand CloseCommand     = new CompositeCommand(true);
  15:     public static CompositeCommand CloseAllCommand  = new CompositeCommand(true);
  16:  
  17:     #endregion
  18: }

Y una clase que ejerce de proxy para acceder a los comandos ( según lo que he visto en la documentación de CompositeWPF esta clase debería servir también para facilitar los tests unitarios

   1: public class GeneralCommandsProxy
   2: {
   3:     #region · Properties ·
   4:  
   5:     public virtual CompositeCommand AddNewCommand
   6:     {
   7:         get { return GeneralCommands.AddNewCommand; }
   8:     }
   9:  
  10:     public virtual CompositeCommand EditCommand
  11:     {
  12:         get { return GeneralCommands.EditCommand; }
  13:     }
  14:  
  15:     public virtual CompositeCommand DeleteCommand
  16:     {
  17:         get { return GeneralCommands.DeleteCommand; }
  18:     }
  19:  
  20:     public virtual CompositeCommand SaveCommand
  21:     {
  22:         get { return GeneralCommands.SaveCommand; }
  23:     }
  24:  
  25:     public virtual CompositeCommand SaveAllCommand
  26:     {
  27:         get { return GeneralCommands.SaveAllCommand; }
  28:     }
  29:  
  30:     public virtual CompositeCommand DiscardCommand
  31:     {
  32:         get { return GeneralCommands.DiscardCommand; }
  33:     }
  34:  
  35:     public virtual CompositeCommand RefreshCommand
  36:     {
  37:         get { return GeneralCommands.RefreshCommand; }
  38:     }
  39:  
  40:     public virtual CompositeCommand SearchCommand
  41:     {
  42:         get { return GeneralCommands.SearchCommand; }
  43:     }
  44:  
  45:     public virtual CompositeCommand PrintCommand
  46:     {
  47:         get { return GeneralCommands.PrintCommand; }
  48:     }
  49:  
  50:     public virtual CompositeCommand CloseCommand
  51:     {
  52:         get { return GeneralCommands.CloseCommand; }
  53:     }
  54:  
  55:     public virtual CompositeCommand CloseAllCommand
  56:     {
  57:         get { return GeneralCommands.CloseAllCommand; }
  58:     }
  59:  
  60:     #endregion
  61: }

Como estoy usando ( o intentándolo ) el patro Model-View-ViewModel para la implementación de las vistas, la ejecución de las acciones se realiza en la clase ViewModel, que es una clase abstract para las implementaciones del ViewModel de cada vista de la aplicación.

   1: public abstract class ViewModel<TDataModel> : IViewModel
   2:     where TDataModel : DataTransferObject, new()

Dispone de propiedades definidas como DelegateCommand<object> ( DelegateCommand también pertenece a CompositeWPF ) por cada uno de los comandos disponibles

   1: /// <summary>
   2: /// Gets or sets the add new command.
   3: /// </summary>
   4: /// <value>The add new command.</value>
   5: public DelegateCommand<object> AddNewCommand
   6: {
   7:     get;
   8:     private set;
   9: }
  10:  
  11: /// <summary>
  12: /// Gets or sets the edit command.
  13: /// </summary>
  14: /// <value>The edit command.</value>
  15: public DelegateCommand<object> EditCommand
  16: {
  17:     get;
  18:     private set;
  19: }
  20:  
  21: /// <summary>
  22: /// Gets or sets the delete command.
  23: /// </summary>
  24: /// <value>The delete command.</value>
  25: public DelegateCommand<object> DeleteCommand
  26: {
  27:     get;
  28:     private set;
  29: }
  30:  
  31: /// <summary>
  32: /// Gets or sets the save command.
  33: /// </summary>
  34: /// <value>The save command.</value>
  35: public DelegateCommand<object> SaveCommand
  36: {
  37:     get;
  38:     private set;
  39: }
  40:  
  41: /// <summary>
  42: /// Gets or sets the discard command.
  43: /// </summary>
  44: /// <value>The discard command.</value>
  45: public DelegateCommand<object> DiscardCommand
  46: {
  47:     get;
  48:     private set;
  49: }
  50:  
  51: /// <summary>
  52: /// Gets or sets the refresh command.
  53: /// </summary>
  54: /// <value>The refresh command.</value>
  55: public DelegateCommand<object> RefreshCommand
  56: {
  57:     get;
  58:     private set;
  59: }
  60:  
  61: /// <summary>
  62: /// Gets or sets the search command.
  63: /// </summary>
  64: /// <value>The search command.</value>
  65: public DelegateCommand<object> SearchCommand
  66: {
  67:     get;
  68:     private set;
  69: }
  70:  
  71: /// <summary>
  72: /// Gets or sets the print command.
  73: /// </summary>
  74: /// <value>The print command.</value>
  75: public DelegateCommand<object> PrintCommand
  76: {
  77:     get;
  78:     private set;
  79: }
  80:  
  81:  
  82: /// <summary>
  83: /// Gets or sets the close command.
  84: /// </summary>
  85: /// <value>The close command.</value>
  86: public DelegateCommand<object> CloseCommand
  87: {
  88:     get;
  89:     private set;
  90: }
  91:  
  92: /// <summary>
  93: /// Gets or sets the close all command.
  94: /// </summary>
  95: /// <value>The close all command.</value>
  96: public DelegateCommand<object> CloseAllCommand
  97: {
  98:     get;
  99:     private set;
 100: }

Además dispone de métodos para inicializar y registrar los comandos

   1: /// <summary>
   2: /// Initializes the commands.
   3: /// </summary>
   4: protected virtual void InitializeCommands()
   5: {
   6:     this.commandProxy   = new GeneralCommandsProxy();
   7:     this.AddNewCommand  = new DelegateCommand<object>(AddNew, CanAddNew);
   8:     this.EditCommand    = new DelegateCommand<object>(Edit, CanEdit);
   9:     this.DeleteCommand  = new DelegateCommand<object>(Delete, CanDelete);
  10:     this.SaveCommand    = new DelegateCommand<object>(Save, CanSave);
  11:     this.DiscardCommand = new DelegateCommand<object>(Discard, CanDiscard);
  12:     this.RefreshCommand = new DelegateCommand<object>(Refresh, CanRefresh);
  13:     this.SearchCommand  = new DelegateCommand<object>(Search, CanSearch);
  14:     this.PrintCommand   = new DelegateCommand<object>(Print, CanPrint);
  15:     this.CloseCommand   = new DelegateCommand<object>(Close, CanClose);
  16: }
  17:  
  18: /// <summary>
  19: /// Registers the commands.
  20: /// </summary>
  21: protected virtual void RegisterCommands()
  22: {
  23:     this.commandProxy.AddNewCommand.RegisterCommand(this.AddNewCommand);
  24:     this.commandProxy.EditCommand.RegisterCommand(this.EditCommand);
  25:     this.commandProxy.DeleteCommand.RegisterCommand(this.DeleteCommand);
  26:     this.commandProxy.SaveCommand.RegisterCommand(this.SaveCommand);
  27:     this.commandProxy.DiscardCommand.RegisterCommand(this.DiscardCommand);
  28:     this.commandProxy.RefreshCommand.RegisterCommand(this.RefreshCommand);
  29:     this.commandProxy.SearchCommand.RegisterCommand(this.SearchCommand);
  30:     this.commandProxy.PrintCommand.RegisterCommand(this.PrintCommand);
  31:     this.commandProxy.CloseCommand.RegisterCommand(this.CloseCommand);
  32: }

Los métodos que se pasan como parámetro del DelegateCommand sirven para ejecutar la acción ligada al comando y indicar si es posible o no su ejecución, para actualizar el estado de ejecución de los comandos la clase dispone de un método más ( que los actualiza todos en bloque aunque también puede ser que en algún momento se necesite hacerlo de forma individual )

   1: private void UpdateAllowedUserActions()
   2: {
   3:     this.AddNewCommand.RaiseCanExecuteChanged();
   4:     this.EditCommand.RaiseCanExecuteChanged();
   5:     this.DeleteCommand.RaiseCanExecuteChanged();
   6:     this.SaveCommand.RaiseCanExecuteChanged();
   7:     this.DiscardCommand.RaiseCanExecuteChanged();
   8:     this.RefreshCommand.RaiseCanExecuteChanged();
   9:     this.SearchCommand.RaiseCanExecuteChanged();
  10:     this.PrintCommand.RaiseCanExecuteChanged();
  11:     this.CloseCommand.RaiseCanExecuteChanged();
  12: }

A modo de ejemplo veamos como están implementados los métodos Refresh y CanRefresh

   1: protected virtual bool CanRefresh(object obj)
   2: {
   3:     return (this.ViewModelState == ViewModelStateType.Default &&
   4:             this.RefreshCommand.IsActive);
   5: }
   6:  
   7: protected virtual void Refresh(object obj)
   8: {
   9:     if (this.RefreshCommand.IsActive)
  10:     {
  11:         if (this.ViewModelState == ViewModelStateType.Default)
  12:         {
  13:             this.ViewModelState = ViewModelStateType.Busy;
  14:  
  15:             if (!ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
  16:                 { 
  17:                     this.OnRefresh(new ServiceResponse<TDataModel>()); 
  18:                 })))
  19:             {
  20:                 this.ViewModelState = ViewModelStateType.Invalid;
  21:             }
  22:         }
  23:     }
  24: }

El comando de Refresh sirve para actualizar los datos en pantalla la acción solo debe estar disponible cuando el estado de la vista sea el estado por defecto ( Consulta ) y que además el comando este activo ( salvo los comandos de Cerrar Todo ( CloseAll ) y Guardar Todo ( SaveAll ) el resto son comandos que sólo deben estar disponibles para la vista activa ( en otro post intentaré explicar como se controla esto ).

El método Refresh comprueba que el comando está activo y el estado actual de la vista, si son correctos ejecuta la acción a realizar de forma asíncrona ( esto también quedará para un post posterior ).

Los métodos para el resto de los comandos están implementados de la misma forma, lo que cambia son las comprobaciones a realizar, por ejemplo el comando de Guardar ( Save ) sólo se debe de poder ejecutar cuando la vista esté en estado de Añadir ( AddingNew ) o Editar ( Editing ), que además haya cambios en los datos y que los datos sean válidos.

   1: protected virtual bool CanSave(object obj)
   2: {
   3:     return (this.ViewModelState == ViewModelStateType.AddingNew ||
   4:             (this.ViewModelState == ViewModelStateType.Editing && this.IsDirty) &&
   5:             this.SaveCommand.IsActive && this.IsValid);
   6: }

Responder

Por favor, inicia sesión con uno de estos métodos para publicar tu comentario:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s