Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF - How to force a Command to re-evaluate 'CanExecute' via its CommandBindings

Not the prettiest in the book, but you can use the CommandManager to invalidate all commandbinding:

CommandManager.InvalidateRequerySuggested();

See more info on MSDN


For anyone who comes across this later; If you happen to be using MVVM and Prism, then Prism's DelegateCommand implementation of ICommand provides a .RaiseCanExecuteChanged() method to do this.


I couldnt use CommandManager.InvalidateRequerySuggested(); because I was getting performance hit.

I have used MVVM Helper's Delegating command, which looks like below (i have tweaked it a bit for our req). you have to call command.RaiseCanExecuteChanged() from VM

public event EventHandler CanExecuteChanged
{
    add
    {
        _internalCanExecuteChanged += value;
        CommandManager.RequerySuggested += value;
    }
    remove
    {
        _internalCanExecuteChanged -= value;
        CommandManager.RequerySuggested -= value;
    }
}

/// <summary>
/// This method can be used to raise the CanExecuteChanged handler.
/// This will force WPF to re-query the status of this command directly.
/// </summary>
public void RaiseCanExecuteChanged()
{
    if (canExecute != null)
        OnCanExecuteChanged();
}

/// <summary>
/// This method is used to walk the delegate chain and well WPF that
/// our command execution status has changed.
/// </summary>
protected virtual void OnCanExecuteChanged()
{
    EventHandler eCanExecuteChanged = _internalCanExecuteChanged;
    if (eCanExecuteChanged != null)
        eCanExecuteChanged(this, EventArgs.Empty);
}

If you have rolled your own class that implements ICommand you can lose a lot of the automatic status updates forcing you to rely on manual refreshing more than should be needed. It can also break InvalidateRequerySuggested(). The problem is that a simple ICommand implementation fails to link the new command to the CommandManager.

The solution is to use the following:

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

This way subscribers attach to CommandManager rather than your class and can properly participate in command status changes.


I've implemented a solution to handle property dependency on commands, here the link https://stackoverflow.com/a/30394333/1716620

thanks to that you'll end up having a command like this:

this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
    //execute
    () => {
      Console.Write("EXECUTED");
    },
    //can execute
    () => {
      Console.Write("Checking Validity");
       return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
    },
    //properties to watch
    (p) => new { p.PropertyX, p.PropertyY }
 );