Search code examples
c#.netwpfmvvmicommand

ICommand MVVM implementation


So in this particular MVVM implementation I'm doing, I need several commands. I really got tired of implementing the ICommand classes one by one, so I came up with a solution, but I don't know how good it is, so the input of any WPF expert here will be greatly appreciated. And if you could provide a better solution, even better.

What I did is a single ICommand class and two delegates which take an object as a parameter, one delegate is void (for OnExecute), the other bool (for OnCanExecute). So in the constructor of my ICommand (which is called by the ViewModel class) I send the two methods, and on each ICommand method I invoke the delegates' methods.

It works really good, but I'm not sure if this is a bad way to do it, or if there's a better way. Below is the complete code, any input will be greatly appreciated, even negative, but please be constructive.

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");
    }
}

ICommand:

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
}

Solution

  • This is almost identical to how Karl Shifflet demonstrated a RelayCommand, where Execute fires a predetermined Action<T>. A top-notch solution, if you ask me.

    public class RelayCommand : ICommand
    {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _execute;
    
        public RelayCommand(Predicate<object> canExecute, Action<object> execute)
        {
            _canExecute = canExecute;
            _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);
        }
    }
    

    This could then be used as...

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

    Read more:
    Josh Smith (introducer of RelayCommand): Patterns - WPF Apps With The MVVM Design Pattern