39

I got the following code from Josh Smith's MVVM tutorial.

Can anyone provide a quick explanation of what this code actually does?

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

I can't understand two things:

  1. what does the CanExecuteChanged event do?
  2. what does the CommandManager.RequerySuggested do?

The above code is from the RelayCommand Class from here.

Kev
  • 118,037
  • 53
  • 300
  • 385
Aryan SuryaWansi
  • 2,691
  • 10
  • 31
  • 33

3 Answers3

53
  1. CanExecuteChanged notifies any command sources (like a Button or MenuItem) that are bound to that ICommand that the value returned by CanExecute has changed. Command sources care about this because they generally need to update their status accordingly (eg. a Button will disable itself if CanExecute() returns false).
  2. The CommandManager.RequerySuggested event is raised whenever the CommandManager thinks that something has changed that will affect the ability of commands to execute. This might be a change of focus, for example. Turns out that this event fires a lot.

So, in essence, what this bit of code does is ensure that whenever the command manager thinks a command's ability to execute has changed, the command will raise CanExecuteChanged even if it hasn't actually changed.

I actually dislike this approach to implementing ICommand.CanExecuteChanged - it feels lazy and isn't entirely reliable. I prefer a much more fine-grained approach where the command exposes a method (eg. RaiseCanExecuteChanged()) you can call to raise CanExecuteChanged, then you call this at the appropriate times from your view model.

For example, if you have a command that deletes the currently selected customer, it would have a CanExecute() handler that returns true only if there is a customer selected. You would therefore call RaiseCanExecuteChanged whenever the selected customer changes.

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • 1
    really nice answer thank you so much for answering...btw,,could you please suggest me the link or any implemented example of `RaiseCanExecuteChanged()`. – Aryan SuryaWansi Jul 11 '11 at 08:54
  • @Kent Boogaart: I tried Prism's `DelegateCommand`, with `RaiseCanExecuteChanged` and thought it would be a better choice over `CommandManager.InvalidateRequerySuggested`. However, it's not working correctly, when I call `MyCommand.RaiseCanExecuteChanged()`, the according `CanMyCommandExecute` method never get called... Any suggestions? – Bolu Jul 12 '11 at 10:43
  • Great answer! Also, it doesn't seem wise to use the command manager for firing the `CanExecuteChanged` event if the command's `CanExecute()` predicate has a lot of work to do. – Nicholas Miller Jul 30 '14 at 13:51
6
  • RoutedCommands can automatically notify if their CanExecute has changed, since we are implementing ICommand here, which the WPF system doesn't know about, we wire them to CommandManager's RequerySuggested event.
  • Now this event is called quite often by the WPF system when the focus changes, any control is edited etc. Hence in turn CanExecuteChanged is raised. As your button is listening to this event it will reinvoke CanExecute to know the latest status.

Here is an article that might be of interest.

anivas
  • 6,437
  • 6
  • 37
  • 45
1

Implementing RaiseCanExecuteChanged()

Command Class:

public class CommandBase : ICommand
{
    Action _TargetExecuteMethod;
    Func<bool> _TargetCanExecuteMethod;

    public CommandBase(Action executeMethod)
    {
        _TargetExecuteMethod = executeMethod;
    }

    public CommandBase(Action executeMethod, Func<bool> canExecuteMethod)
    {
        _TargetExecuteMethod = executeMethod;
        _TargetCanExecuteMethod = canExecuteMethod;
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }

    public event EventHandler CanExecuteChanged

    public bool CanExecute(object parameter)
    {             

        if (_TargetCanExecuteMethod != null)
        {
            return _TargetCanExecuteMethod();
        }

        if (_TargetExecuteMethod != null)
        {
            return true;
        }                       
                    
        return false;
    }

    public void Execute(object parameter)
    {
        if (_TargetExecuteMethod != null)
        {
            _TargetExecuteMethod();
        }
    }
}

View:

<TextBox Grid.Row="3" Text="{Binding TextValue, UpdateSourceTrigger=PropertyChanged }"/>
<Button Content="DoSomething" Command="{Binding DoSomething}" />

ViewModel class:

public class MyclassViewModel
{
     private string textValue;

     public String TextValue
     {
        get {                
            return textValue; 
        }
        set {               
            textValue = value;
            DoSomething.RaiseCanExecuteChanged();
        }
     }

     public CommandBase DoSomething { get; set; }

     public MyclassViewModel() //Constructor
     {            
        DoSomething = new CommandBase(OnDoSomething, CanDoSomething);            
     }

     private bool CanDoSomething()
     {
        if (TextValue?.Length > 5)
        {
            return true;
        }
        return false;
     }

    private void OnDoSomething()
    {
        //Do Something
    }
}