I am making some custom ICommand implementation of my own and I see A LOT of implementations going like this:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
protected void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
As far as I can see this is poorly optimized code since calling RaiseCanExecuteChanged()
will trigger ALL commands in the UI to check their ICommand.CanExecute
status, when usually we just want one of them to verify it.
I think I read once this is the main code of some WPF ICommands like RoutedCommand
, and for them it makes sense because they want to revalidate all ICommands automatically once some control loses focus and things like this, but still I don't understand why people repeat this pattern for their own ICommand
implementations.
The code I have in mind is a simple event invocation such as:
public event EventHandler CanExecuteChanged;
protected void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
I tested and this works, so Why all the examples on the web don't implement something as simple as this? Am I missing something?
I have read about memory leak problems using strong references in regular events, where the CommandManager
only uses WeakReferences
, which is good in case the View is Garbage Collected, but still, aren't there any solutions that won't compromise performance over memory footprint?
Why all the examples on the web don't implement something as simple as this? Am I missing something?
I'm guessing it's mostly due to laziness... What you propose is indeed a better (more efficient) implementation. However, it's not complete: you still need to subscribe to CommandManager.RequerySuggested
to raise CanExecuteChanged
on the command.
Very simply - if you are performing heavy work in ICommand.CanExecute()
then you are using Commands
very badly. If you follow that rule there should in fact be no serious performance implication to calling CommandManager.InvalidateRequerySuggested()
.
Pragmatically, it's a much easier implementation than what you've suggested.
Personally, I rather call CommandManager.InvalidateRequerySuggested()
in a particular ViewModel
when a property changes so that the feedback to the user is instantaneous (i.e. enabling a button as soon as a form is completed/valid).
This question is quite old, it's 2019 now, but I have found another reason to use CommandManager.InvalidateRequerySuggested().
I have written my own custom ICommand class for a WPF application, in which I invoked CanExecuteChanged directly in the first place like this.
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, null);
}
My WPF application makes heavy use of different threads and when the above method is called from another thread then the main UI thread, it throws no error, it is just kind of ignored. It was getting even worse, when I found out, that all code lines inside the calling method were skipped, which led to strange results.
I don't know exactly, but I guess the reason was, that CanExecuteChanged led to changes in my UI, which must not be changed from another thread.
However - the moment when I changed my ICommand to CommandManager.InvalidateRequerySuggested(), there was no more problem. It seems that CommandManager.InvalidateRequerySuggested() can be called from any thread and the UI still gets updated.
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
I thought this could be a valueable answer, because I debugged this issue for 3 hours, before I came to this solution. The problem finding this issue was, that there were no errors thrown while debugging. The code was just skipped. Very strange behaviour.
This is an answer to this answer. It is true that the CanExecuteChanged?.Invoke(this, null);
has to be called by the main UI thread.
Simply write it like follows:
public void RaiseCanExecuteChanged()
{
Application.Current.Dispatcher.Invoke(() => CanExecuteChanged?.Invoke(this, null));
}
This solves your problem and you can requery just one command. It is however true that you should nevertheless make your CanExecute
-Method as fast as possible, as it will anyways be periodically executed.
It is best to have CanExecute just consist of a single return foo;
where foo
is a field you can set right before you call CommandManager.InvalidateRequerySuggested();
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With