Programatic re-evaluation of MVVM command's "can execute" state
Asked Answered
T

1

10

I'm writing a WPF application using the MVVM pattern, based on the following article: WPF Apps With The Model-View-ViewModel Design Pattern

I have two buttons on my View with the buttons' "Command" property bound (with data binding) to a given instance of the RelayCommand class (see "Figure 3 The RelayCommand Class" from the article above). The RelayCommand class has support for checking whether the given command can be executed.

WPF automatically disables buttons whose command cannot be executed.

Each of my commands (in the ViewModel class) start a background operation, and the command cannot be executed again until the background operation is finished. The RelayCommand instances have information whether the background operation is still working or it is finished.

My problem is the following: after pressing the any of the buttons, the buttons automaticaly go disabled (which is OK) because the background operation started and the command cannot be executed until it is finished, but after the operation had finished, the buttons don't go enabled automatically because their command's "can be executed" predicate is not automatically reevaluated. The reevaluation can be manually triggered by having the application loose and regain focus (by pressing ALT+TAB). After doing this trick, the buttons get enabled once again.

How can I programatically reevaluate the buttons' command's "can execute" state?

Torse answered 6/6, 2010 at 15:40 Comment(0)
M
20

You can call InvalidateRequerySuggested on the CommandManager to notify that CanExecute should be re-queried:

CommandManager.InvalidateRequerySuggested();

http://msdn.microsoft.com/en-us/library/system.windows.input.commandmanager.invalidaterequerysuggested.aspx

This does depend on whether the particular ICommand implementation has properly implemented the ICommand.CanExecuteChanged pattern, so YMMV.

Update

For instance, I use Prism which has it's own base implementation ICommand: DelegateCommand. I find that calling RaiseCanExecuteChanged(), on a DelegateCommand in Prism work for me.

Update 2

And make sure that you are calling InvalidateRequerySuggested() on the UI thread. Use the Dispatcher if necessary to make the call.

Meso answered 6/6, 2010 at 15:59 Comment(6)
This is the very same thing that my first guess was, but, for some unknown reason to me, it doesn't seem to work. I'm calling this static method ("CommandManager.InvalidateRequerySuggested") every time the state of the program (like Idle, Working, PendingStop etc.) changes, it's only this state property that is used in the canExecute handlers. Still, it doesn't seem to work (though I agree that it should). BTW, go and check the ICommand implementation, it's in the article I referenced before, figure 3.Torse
Yes it does really depend on how ICommand has been implemented. I have updated my answer detailing how I get CanExecute re-queried in Prism. I will look at the article you are following.Meso
Is this something possibly to do with executing InvalidateRequerySuggested() on a non-UI thread? Try using the Dispatcher to call it, so that it is called on the UI thread.Meso
Good call, I'm gonna try calling it with the Dispatcher.Torse
If InvalidateRequerySuggested() only works on the UI thread, shouldn't the documentation say that (it doesn't)? It would certainly be easy enough for the implementation to push the call the the UI thread -- it doesn't make sense for 100 clients to each make the call thru the Dispatcher rather than having the one CommandManager implementation do it.Subeditor
MMVM Light also has RaiseCanExecuteChanged() method on RelayCommand implementation.Klemperer

© 2022 - 2024 — McMap. All rights reserved.