Why CanxxxCommandExecute is fired so much times?

2019-08-12 18:16发布

问题:

I've got some problems with Commands and their CanXXXExecute... I was thinking it was a Catel (MVVM framework) problem but I've tested myself with a standard WPF application and it still happen..

In the example I'm posting I've got the "CanClickCommandExecute" method called 2 times at load (one at constructor and I agree with this, the other one I think at view's load) and 3 times when I click the button!!

Here's the XAML

<Window x:Class="StandardWPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Button Command="{Binding ClickCommand}" Content="ClickMe!" Background="Teal"></Button>
</Grid>

The .cs

public partial class MainWindow : Window
{

    public ICommand ClickCommand { get; private set; }
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;

        ClickCommand = new RelayCommand(OnClickCommandExecute,CanClickCommandExecute);
    }

    private bool CanClickCommandExecute()
    {
        Debug.WriteLine("Standard OnClickCommandExecute fired!");
        return true;
    }

    private void OnClickCommandExecute()
    {
      //something weird in there!
    }
}

The RelayCommand (taken from here)

public class RelayCommand : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    private Action methodToExecute;
    private Func<bool> canExecuteEvaluator;
    public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
    {
        this.methodToExecute = methodToExecute;
        this.canExecuteEvaluator = canExecuteEvaluator;
    }
    public RelayCommand(Action methodToExecute)
        : this(methodToExecute, null)
    {
    }
    public bool CanExecute(object parameter)
    {
        if (this.canExecuteEvaluator == null)
        {
            return true;
        }
        else
        {
            bool result = this.canExecuteEvaluator.Invoke();
            return result;
        }
    }
    public void Execute(object parameter)
    {
        this.methodToExecute.Invoke();
    }
}

Why this happen? How can I spot what's happening? In my real case I use the CanXXX to perform validation...in that case I got validation fired a lot of times and on some object can take a lot of time

回答1:

Why CanxxxCommandExecute is fired so much times?

Because you're raising the CanExecuteChanged method several times, and so CanExecute is evaluated by wpf several times.

You're not directly raising the event yourself, but CommandManager.RequerySuggested does it.

As the documentation says Occurs when the CommandManager detects conditions that might change the ability of a command to execute. CommandManager.RequerySuggested will guess the possible events which might need to update the button's state. It may happen many times, for instance minimizing the app will trigger it; etc.

If you want to take control over when CanExecute is called, then raise the event manually. Don't use CommandManager.RequerySuggested.



回答2:

If you look at the .NET Framework source code, CommandManager doesn't detect conditions by itself, rather when Keyboard.KeyUpEvent, Mouse.MouseUpEvent, Keyboard.GotKeyboardFocusEvent, or Keyboard.LostKeyboardFocusEvent occurs, it will fire the CanExecute method; obviously, this can happen multiple times.