Sunday, October 12, 2014

WPF and MVVM - Commands

To extend the MVVM idea, it is necessary to move more code from code-behind of the View to the ViewModel. In the first part we have seen how the properties from the ViewModel can be binded to the View and how changes can be notified.

Now we will see how to move click event handler from code-behind of the View to the ViewModel. Therefore the Command property in XAML of the View is used instead of the click event. The property is set to an instance of an implementation of ICommand. For that we want to have a reusable implementation. One way is to use the DelegateCommand that is also used in the Prism Class Library, the usage is described here. But we want to focus on the simpler implementation of RelayCommand. The RelayCommand builds on WPF Commanding.
public class RelayCommand : ICommand
{
    private readonly Action<object> _Execute;
    private readonly Func<objectbool> _CanExecute;
 
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
 
    }
 
    public RelayCommand(Action<object> execute, Func<objectbool> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute""Execute cannot be null.");
        }
 
        _Execute = execute;
        _CanExecute = canExecute;
    }
 
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
 
    public void Execute(object parameter)
    {
        _Execute(parameter);
    }
 
    public bool CanExecute(object parameter)
    {
        if (_CanExecute == null)
        {
            return true;
        }
 
        return _CanExecute(parameter);
    }
}
The RelayCommand implements the interface ICommand. Therefore we have to implement the methods Execute and CanExecute. These methods call other methods that you have passed as Action and Func generic delegate to the constructor of the RelayCommand class. Furthermore we have to implement the CanExecuteChanged event. Execute can only be called if CanExecute returns true. If CanExecute depends on one or more properties, then on each change of one of these properties CanExecuteChanged have to be fired so that CanExecute is checked again. To simplify and to automate firing the event, the event is hooked to the static RequerySuggested event of the CommandManager. Doing so the CanExecuteChanged event is called after each action in the UI. In some cases you want to call the CanExecuteChanged event manually (such as CanExecute doesn't depends only on UI properties). Therefore the event hook can be removed and a method is added that can be called manually.
public event EventHandler CanExecuteChanged;
public void OnCanExecuteChanged()
{
    if (CanExecuteChanged != null)
    {
        CanExecuteChanged(thisnew EventArgs());
    }
}
Using the RelayCommand we can move click event handler to the ViewModel. Therefore we add a property whose type is of ICommand.
public ICommand LoginCommand
{
    get;
    private set;
}
The ICommand will be instantiated in the constructor.
LoginCommand = new RelayCommand(Login, CanLogin);
The Execute method of the RelayCommand will call Login.
private void Login(object parameter)
{
    var passwordContainer = parameter as IHavePassword;
    if (passwordContainer != null)
    {
        var secureString = passwordContainer.Password;
        PasswordInVM = ConvertToUnsecureString(secureString);
    }
}
While the CanExecute method of the RelayCommand will call CanLogin.
private bool CanLogin(object parameter)
{
    return !string.IsNullOrEmpty(UserName);
}
At last we need only to add the right Command by binding to the View. We can also pass through a parameter by using CommandParameter. The parameter is set to both methods, Execute and CanExecute.
<Button x:Name="Login"
        Content="Login"
        Command="{Binding LoginCommand}"
        CommandParameter="{Binding ElementName=This}" />
With the RelayCommand class we have our second component of our own MVVM-Framework.

Further Posts

  1. WPF and MVVM - Fundamentals
  2. WPF and MVVM - Commands
  3. WPF and MVVM - Events

1 comment:

  1. Chriga, Nice article period. I chose to use prism framework over wheel reinvention and I just do not like depending on the CommandManager, its just scary how erratic it is. What do you think of prism or any of the other MVVM frameworks out there?

    ReplyDelete