maxp
maxp

Reputation: 25141

Forcing Reevaluation on ICommand.CanExecute

In WPF XAML, I've got a Button's Command property working with an implementation of a class implementing ICommand.

In this implementation, I don't have my CanExecuteChanged event wired up to use CommandManager.RequerySuggested - I want to have control over when CanExecute is called, and using this calls it way more often than necessary.

The only other way I can get ICommand.CanExecute to re-evaluate is to use something like:

public void InvokeCanExecute()
{
    CanExecuteChanged.Invoke(this, new EventArgs());
}

In my class implementing the ICommand.

This seems really nasty - am I missing something obvious? I've tried invoking the re-evaluation using PropertyChanged but that doesn't seem to work.

Upvotes: 2

Views: 628

Answers (2)

andreask
andreask

Reputation: 4298

It's true that - if you don't want to use CommandManager.RequerySuggested, which indeed might call CanExecute more often than necessary - you need to explicitly invoke your own InvokeCanExecute method in order to force the command to re-evaluate its CanExecute condition.

However, in most cases the CanExecute condition will depend on public (bindable) properties, meaning properties that raise the PropertyChanged event to indicate that their value has changed - it is possible to hook into this event, in order to automatically call InvokeCanExecute whenever one of the properties the command depends on has changed. For an example of how to implement such a command, see this guy's blog post (if I'm not mistaken, this approach is implemented e.g. as part of the MVVM Light toolkit).

Instantiating a command using this approach would look somewhat like the following:

SaveCommand = new RelayCommand(() => { /* do some stuff; */ }, 
                               () => !string.IsNullOrEmpty(Name), 
                               this, () => Name);

Since the command's CanExecute condition (which checks whether Name is empty) depends on the Name property, it needs to be re-evaluated whenever Name's content changes. You simply pass a reference to the Name property to the command's constructor, and InvokeCanExecute will automatically be called whenever the value of Name changes.

In theory, it is possible to go one step further and let the command itself check on which properties it depends - if you're interested in this approach, check out one of my blog articles, but note that this one heavily depends on reflection so it always depends on the detailed use-case whether this approach is feasible or not. A sample implementation of this solution is included in the MVVMbasics framework (disclaimer: published by me). In this case, you could reduce the command initialization code to:

SaveCommand = CreateRelayCommand(() => { /* do some stuff; */ }, 
                                () => !string.IsNullOrEmpty(Name));

Upvotes: 2

Michael Domashchenko
Michael Domashchenko

Reputation: 1480

No, you are not really missing anything. Here's a similar question that recommends the same approach you're taking: What is the actual task of CanExecuteChanged and CommandManager.RequerySuggested?.

You can make your method a little bit more robust though:

public void InvokeCanExecute()
{
    var handler = CanExecuteChanged;
    if (handler != null)
    {
        handler(this, new EventArgs());
    }
}

Upvotes: 1

Related Questions