简体   繁体   中英

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.

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

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 eg 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));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM