3

Our UI currently has a lot of controls that are bound to commands with some complex CanExecutes. The problem we are facing is that whenever CommandManager determines that the UI needs to be re-evaulated, all commands run their CanExecute, which in turn, causes quite a performance hit for specific scenarios.

Reading this post: How does CommandManager.RequerySuggested work?

It seems that the CommandManager will re-evaulate on simple key down, mouse move events, etc. Is there a way to prevent this from happening, and instead, have the command manager re-evaluate when manually invoked?

Community
  • 1
  • 1
d.moncada
  • 16,900
  • 5
  • 53
  • 82
  • 2
    I don't think you can. `CanExecute` should not contain any complex functionality. Maybe you could create a bool variable that will tell you if it can be executed or not and change this variable when the conditions change? – vesan Jan 12 '15 at 22:42
  • And when/where would you invoke it? – Dzyann Jan 12 '15 at 22:43
  • @Dzyann, a good scenario would be when a specific event is finished running (or gets raised), then have all commands 're-evaluate' – d.moncada Jan 12 '15 at 22:45
  • I would leave everything as it is and add additional validation to the specific places that are causing issues. Consider that in the future changing the default behavior of the CommandManager can cause all kind of issues. An unaware programmer adding logic to certain places and never getting fired. I would imagine that only certain parts of your code are causing performance issues. – Dzyann Jan 12 '15 at 23:26

1 Answers1

1

A solution might be to implement a simpler version of the RelayCommand class that simply stores the event handlers itself and exposes a public method to fire them when appropriate:

public class RelayCommand : ICommand
{
    public event EventHandler CanExecuteChanged;

    // Further ICommand implementation omitted...

    public void Invalidate()
    {
        var handler = this.CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

You then call the following in your viewModel to re-evaluate the command:

fooCommand.Invalidate();

Of course, this leaves you with the opposite problem that you now have to manually re-evaluate all commands...

Edit

To elaborate on the comments, most RelayCommand's implement the CanExecuteChanged event like this:

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

When the UI subscribes to the command's CanExecuteChanged event it is actually being indirectly subscribed to the CommandManager.RequerySuggested event which is why your CanExecute method is being called every time the CommandManager suggest a re-query.

The simpler RelayCommand I have suggested avoids this problem by not subscribing to the CommandManager.RequerySuggested event.

Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100
  • Ben, I can see how this will provide a mechanism for manually invoking, but I do not understand how this prevent CommandManager from invoking automatically based on UI interaction. – d.moncada Jan 12 '15 at 22:55
  • It won't stop the `CommandManager` but it doesn't matter. The simplified `RelayCommand` does not pass any registered event handlers through to the `CommandManager` meaning the UI won't re-evaluate the command every time the `RequerySuggested` event is fired. – Benjamin Gale Jan 12 '15 at 23:08
  • Ben, this provided a mechanism for my issue. thanks! – d.moncada Jan 13 '15 at 21:32