Implementación WPF ICommand MVVM

Entonces, en esta implementación particular de MVVM que estoy haciendo, necesito varios comandos. Realmente me cansé de implementar las clases de ICommand una a una, así que se me ocurrió una solución, pero no sé lo bueno que es, por lo que la contribución de cualquier experto de WPF será muy apreciada. Y si pudiera proporcionar una mejor solución, ¡aún mejor!

Lo que hice fue una única clase ICommand y dos delegates que toman un objeto como parámetro, un delegado es nulo (para OnExecute), el otro bool (para OnCanExecute). Entonces en el constructor de mi ICommand (que es llamado por la clase ViewModel) envío los dos métodos, y en cada método ICommand invoco los métodos de los delegates.

Funciona muy bien, pero no estoy seguro si esta es una mala manera de hacerlo, o si hay una mejor manera. A continuación se muestra el código completo, cualquier contribución será muy apreciada, incluso negativa, pero sea constructivo.

¡¡Gracias!!

ViewModel:

public class TestViewModel : DependencyObject { public ICommand Command1 { get; set; } public ICommand Command2 { get; set; } public ICommand Command3 { get; set; } public TestViewModel() { this.Command1 = new TestCommand(ExecuteCommand1, CanExecuteCommand1); this.Command2 = new TestCommand(ExecuteCommand2, CanExecuteCommand2); this.Command3 = new TestCommand(ExecuteCommand3, CanExecuteCommand3); } public bool CanExecuteCommand1(object parameter) { return true; } public void ExecuteCommand1(object parameter) { MessageBox.Show("Executing command 1"); } public bool CanExecuteCommand2(object parameter) { return true; } public void ExecuteCommand2(object parameter) { MessageBox.Show("Executing command 2"); } public bool CanExecuteCommand3(object parameter) { return true; } public void ExecuteCommand3(object parameter) { MessageBox.Show("Executing command 3"); } } 

Yo ordeno:

 public class TestCommand : ICommand { public delegate void ICommandOnExecute(object parameter); public delegate bool ICommandOnCanExecute(object parameter); private ICommandOnExecute _execute; private ICommandOnCanExecute _canExecute; public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod) { _execute = onExecuteMethod; _canExecute = onCanExecuteMethod; } #region ICommand Members public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public bool CanExecute(object parameter) { return _canExecute.Invoke(parameter); } public void Execute(object parameter) { _execute.Invoke(parameter); } #endregion } 

Esto es casi idéntico a cómo Karl Shifflet demostró un RelayCommand , donde Execute dispara una Action predeterminada Action . Una excelente solución, si me preguntas.

 public class RelayCommand : ICommand { private Predicate _canExecute; private Action _execute; public RelayCommand(Predicate canExecute, Action execute) { this._canExecute = canExecute; this._execute = execute; } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public bool CanExecute(object parameter) { return _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } } 

Esto podría ser usado como …

 public class MyViewModel { private ICommand _doSomething; public ICommand DoSomethingCommand { get { if (_doSomething == null) { _doSomething = new RelayCommand( p => this.CanDoSomething, p => this.DoSomeImportantMethod()); } return _doSomething; } } } 

Lee mas:
Josh Smith (introductor de RelayCommand ): Patrones – Aplicaciones WPF con el patrón de diseño MVVM

He escrito este artículo sobre la interfaz de ICommand.

La idea: crear un comando universal que requiera dos delegates: uno se llama cuando se invoca ICommand.Execute (object param) , el segundo comprueba el estado de si se puede ejecutar el comando (ICommand.CanExecute (object param)) .

Requiere el método para cambiar el evento CanExecuteChanged . Se llama desde los elementos de la interfaz de usuario para cambiar el comando CanExecute() estado.

 public class ModelCommand : ICommand { #region Constructors public ModelCommand(Action execute) : this(execute, null) { } public ModelCommand(Action execute, Predicate canExecute) { _execute = execute; _canExecute = canExecute; } #endregion #region ICommand Members public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { return _canExecute != null ? _canExecute(parameter) : true; } public void Execute(object parameter) { if (_execute != null) _execute(parameter); } public void OnCanExecuteChanged() { CanExecuteChanged(this, EventArgs.Empty); } #endregion private readonly Action _execute = null; private readonly Predicate _canExecute = null; } 

Acabo de crear un pequeño ejemplo que muestra cómo implementar comandos en convención sobre el estilo de configuración. Sin embargo, requiere que Reflection.Emit () esté disponible. El código de soporte puede parecer un poco raro, pero una vez escrito puede usarse muchas veces.

Rompecabezas:

 public class SampleViewModel: BaseViewModelStub { public string Name { get; set; } [UiCommand] public void HelloWorld() { MessageBox.Show("Hello World!"); } [UiCommand] public void Print() { MessageBox.Show(String.Concat("Hello, ", Name, "!"), "SampleViewModel"); } public bool CanPrint() { return !String.IsNullOrEmpty(Name); } } 

}

ACTUALIZACIÓN : ahora parece que existen algunas bibliotecas como http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model que resuelven el problema del código estándar de ICommand.

@Carlo Me gusta mucho su implementación de esto, pero quería compartir mi versión y cómo usarla en mi ViewModel

Primero implementa ICommand

 public class Command : ICommand { public delegate void ICommandOnExecute(); public delegate bool ICommandOnCanExecute(); private ICommandOnExecute _execute; private ICommandOnCanExecute _canExecute; public Command(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod = null) { _execute = onExecuteMethod; _canExecute = onCanExecuteMethod; } #region ICommand Members public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public bool CanExecute(object parameter) { return _canExecute?.Invoke() ?? true; } public void Execute(object parameter) { _execute?.Invoke(); } #endregion } 

Tenga en cuenta que he eliminado el parámetro de ICommandOnExecute y ICommandOnCanExecute y he añadido un valor nulo al constructor

Luego para usar en ViewModel

 public Command CommandToRun_WithCheck { get { return new Command(() => { // Code to run }, () => { // Code to check to see if we can run // Return true or false }); } } public Command CommandToRun_NoCheck { get { return new Command(() => { // Code to run }); } } 

Simplemente me parece más limpio ya que no necesito asignar variables y luego crear instancias, todo hecho de una vez.