2

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

advapi
  • 3,661
  • 4
  • 38
  • 73
  • Ok but considering this scenario I'll have the validation called more and more times even if it's totally useless – advapi Sep 11 '15 at 13:09
  • What you see it called *polling* technique. Instead of reacting directly there is a dedicated moment to poll, which may differ in time. If there would be a way to check what command can be executed without calling method it would be so, but for commands `CanXXX" method is the only way to do so, therefore pollings. You shouldn't try to rely on when this method will be called. – Sinatr Sep 11 '15 at 13:13
  • If you use Catel, it will automatically requery the commands for you on property changes instead of using the CommandManager which does it way too often. – Geert van Horrik Sep 11 '15 at 14:10
  • Geert my example was to exclude it was a Catel issue .... as I wrote you on JIRA I've got validation called a lot of time since it's called inside CanValidateXXX method – advapi Sep 11 '15 at 14:40
  • But the reason it's doing this is because you use CommandManager.RequerySuggested, which is being handled on nearly every click, mouse move, key entry, etc. That's why we stopped using that in Catel. – Geert van Horrik Sep 12 '15 at 04:59
  • Hello geertvanhorrik.com, I do so in this sample... in the project I've posted under JIRA no... I got canexecute called several times ( seems one for each property I've ) even if I only charge one prop – advapi Sep 12 '15 at 12:28

2 Answers2

4

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.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • Okok but if I can somehow handle it in my example, a little bit different is with Catel's one since I use it out of the box – advapi Sep 11 '15 at 13:27
  • @advapi not sure what you mean. You have to avoid using `CommandManager.RequerySuggested` inside your `RelayCommand` class. If Catel uses it, then don't use that command (provided by catel). implement your own command particularly for this purpose. – Sriram Sakthivel Sep 11 '15 at 13:35
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.

Allan Elder
  • 4,052
  • 17
  • 19