example commandmanager c# wpf mvvm relaycommand delegatecommand

c# - commandmanager - Simplificación de RelayCommand/DelegateCommand en los modelos de visualización de MVVM de WPF



relay command wpf (6)

Si está ejecutando MVVM y usando comandos, a menudo verá las propiedades de ICommand en ViewModel que están respaldadas por campos privados RelayCommand o DelegateCommand, como este ejemplo del artículo original de MVVM en MSDN :

RelayCommand _saveCommand; public ICommand SaveCommand { get { if (_saveCommand == null) { _saveCommand = new RelayCommand(param => this.Save(), param => this.CanSave ); } return _saveCommand; } }

Sin embargo, esto es un montón de desorden, y hace que la configuración de nuevos comandos sea bastante tediosa (trabajo con algunos desarrolladores veteranos de WinForms que se resisten a todo este tipo de escritura). Así que quería simplificarlo y cavar un poco. Establecí un punto de interrupción en la primera línea del bloque de obtención {} y vi que solo se golpeó cuando mi aplicación se cargó por primera vez. Luego, puedo disparar tantos comandos como quiera y este punto de interrupción nunca recibe un golpe, por lo que quería simplificar esto para eliminar un poco de desorden de mis modelos de vista y notó que el siguiente código funciona de la misma manera:

public ICommand SaveCommand { get { return new RelayCommand(param => this.Save(), param => this.CanSave ); } }

Sin embargo, no sé lo suficiente sobre C # o el recolector de basura para saber si esto podría causar problemas, como la generación excesiva de basura en algunos casos. ¿Esto planteará algún problema?


Cuando expone la propiedad ICommand en su modelo de vista y no tiene un campo de respaldo, esto está bien, siempre y cuando solo se vincule a este campo una vez.

El método GetCommand de CommandWrapper devolverá el comando si ya está creado.


¿Por qué no escribes simplemente?

private readonly RelayCommand _saveCommand = new RelayCommand(param => this.Save(), param => this.CanSave );; public ICommand SaveCommand { get { return _saveCommand; } }


Cuando expone la propiedad ICommand en su modelo de vista y no tiene un campo de respaldo, esto está bien, siempre y cuando solo se vincule a este campo una vez. Básicamente, cuando su formulario se carga y realiza los enlaces iniciales, esta es la única vez que accederá a la propiedad get de su comando.

Hay muchas veces en las que unirás un comando solo una vez.

Si vincula el mismo comando a más de un control, entonces necesitará el campo de respaldo.


Descubrí que necesita la forma original de MSDN si tiene varios controles que invocan los mismos comandos, de lo contrario, cada control incorporará su propio RelayCommand. No me di cuenta de esto porque mi aplicación solo tiene un control por comando.

Así que para simplificar el código en ViewModels, crearé una clase de envoltorio de comando que almacene (y ejemplifique perezosamente) todos los RelayCommands y los coloqué en mi clase ViewModelBase. De esta manera, los usuarios no tienen que instanciar directamente los objetos RelayCommand o DelegateCommand y no necesitan saber nada sobre ellos:

/// <summary> /// Wrapper for command objects, created for convenience to simplify ViewModel code /// </summary> /// <author>Ben Schoepke</author> public class CommandWrapper { private readonly List<DelegateCommand<object>> _commands; // cache all commands as needed /// <summary> /// </summary> public CommandWrapper() { _commands = new List<DelegateCommand<object>>(); } /// <summary> /// Returns the ICommand object that contains the given delegates /// </summary> /// <param name="executeMethod">Defines the method to be called when the command is invoked</param> /// <param name="canExecuteMethod">Defines the method that determines whether the command can execute in its current state. /// Pass null if the command should always be executed.</param> /// <returns>The ICommand object that contains the given delegates</returns> /// <author>Ben Schoepke</author> public ICommand GetCommand(Action<object> executeMethod, Predicate<object> canExecuteMethod) { // Search for command in list of commands var command = (_commands.Where( cachedCommand => cachedCommand.ExecuteMethod.Equals(executeMethod) && cachedCommand.CanExecuteMethod.Equals(canExecuteMethod))) .FirstOrDefault(); // If command is found, return it if (command != null) { return command; } // If command is not found, add it to the list command = new DelegateCommand<object>(executeMethod, canExecuteMethod); _commands.Add(command); return command; } }

Esta clase también tiene una instancia perezosa de la clase ViewModelBase, por lo que ViewModels que no tienen ningún comando evitará las asignaciones adicionales.


Esto es exactamente lo mismo que si ofreciera una propiedad de - digamos entero - que calcula un valor constante. Puede calcularlo para cada llamada en el método get o puede crearlo en la primera llamada y luego almacenarlo en caché para devolver el valor almacenado en caché para llamadas posteriores. Entonces, si se llama al captador a lo sumo una vez, no hace ninguna diferencia, si se llama a menudo, perderá algo de rendimiento (no mucho), pero no tendrá problemas reales.

Personalmente, me gusta abreviar la forma de MSDN de esta manera:

RelayCommand _saveCommand; public ICommand SaveCommand { get { return _saveCommand ?? (_saveCommand = new RelayCommand(param => this.Save(), param => this.CanSave )); } }


Una cosa que hago es dejar que Visual Studio me escriba. Acabo de crear un fragmento de código que me permite crear un RelayCommand escribiendo

rc Tab Guardar Entrar

rc es la pestaña de acceso directo del fragmento de código que carga el texto que escribe lo que quiere y crea todas las demás palabras.

Una vez que vea un fragmento de código y cree el suyo, nunca más volverá :)

Para obtener más información sobre la creación de fragmentos de código: http://msdn.microsoft.com/en-us/library/ms165394.aspx