3

I'm trying to implement a highly responsive UI for my MVVM application, so I've chosen to have all command handlers automatically execute in a BackgroundWorker so they don't block the UI.

But at the same time, I don't want the user to be able to execute the same command while it is still executing in the background. The solution seems obvious:

  1. When Executed handler is invoked, have the CanExecute handler return false
  2. Start the BackgroundWorker async
  3. When BackgroundWorker finishes, have the CanExecute handler return true again.

Problem is, I need to notify WPF after step 1 and again after step 3 that CanExecute has changed and that it should be required. I know I can do it by calling CommandManager.InvalidateRequerySuggested, but that causes CanExecute handlers of all other commands to be requeried as well. Having a lot of commands, that's not good.

Is there a way to ask for requery of a specific command - i.e. the one that is currently being executed?

TIA

aoven
  • 2,248
  • 2
  • 25
  • 36

3 Answers3

6

I think you only need to raise the ICommand.CanExecuteChanged event for these two cases, and the corresponding control should update its IsEnabled state.

gehho
  • 9,049
  • 3
  • 45
  • 59
1

gehho is right, just raise the CanExecuteChanged event. But honestly, I don't know your implementation, but if your CanExecute predicate is simple, I think it will be hard to notice that wpf is requerying. I can't believe you will notice a performance hit. Have you tested performance? I mean let's say you have 20 ICommands in a ViewModel(which is probably too much) if the ICommand.CanExecute points to a boolean field that you are setting to true and false based on the backgroundworker it should take less than nanoseconds. But if the CanExecute is pointed to a method that evaluates, then it may be longer, but I still doubt it would be noticeable.

But hey, I don't know your implementation.

Jose
  • 10,891
  • 19
  • 67
  • 89
1

Use MVVM Helper's `DelegatingCommand' which exposes RaiseCanExecuteChanged() for the Command.

So you can subscribe to RunWorkercompleted event on BackgroundWorker and depending on the kind of work logged (maybe you can return as part of the Result) you call Command.RaiseCanExecuteChanged() from ViewModel.

Bek Raupov
  • 3,782
  • 3
  • 24
  • 42