views:

61

answers:

1

My app is written using the MVVM pattern in WPF, and all of my Buttons use Command bindings to execute code in my model. All commands have code in CanExecute to determine the bound Button's Enabled state. The logic works perfectly, but in all cases, the GUI remains in a disabled state unless I click somewhere else in the GUI.

For example, I have a button called Discard Candy. When I click this button, it launches a process in a threadpool thread, which sets a bool property called Running to true. Since the CanExecute method for Discard Candy's command looks something like this

public bool CanExecute(object parameter)
{
  return !Running;
}

the button will be disabled once the process starts. The problem is that when the process is done, Running gets set to false, but the GUI doesn't update, i.e. Discard Candy doesn't get re-enabled.

However, if I click anywhere in the GUI, like on the window or title bar, the Discard Candy button all of a sudden gets enabled. So the logic works, but something is going on that I just don't understand. Can someone please explain this behavior to me?

EDIT -- so far, it sounds like CommandManager.InvalidateRequerySuggested hasn't helped people. I am going to give it a shot, but at the moment am a little wary of it. I did follow the recommended links, and in doing so decided to read more about the MVVM light toolkit. It sounds very nice -- has anyone here used it and been able to confirm that it does not exhibit the problem I've been seeing so far? Although I plan to try the MVVM light toolkit in the next major rev. of my application, I don't want to redo all of the commanding that I currently have in place, which is why I'll likely start with CommandManager.InvalidateRequerySuggested so we can all get another data point here regarding it's usefulness.

EDIT #2 -- very interesting, the MVVM light toolkit actually relies on CommandManager.InvalidateRequerySuggested in order to support the UI's ability to disable / re-enable commands. The author says:

"Strictly speaking, in WPF, and if your command is bound to a control that is watched by the CommandManager, you shouldn’t have to raise the CanExecuteChanged event yourself. You can let the CommandManager handle the situation. That said, external events might also change the state of the UI. Let’s imagine that the UI should be enabled from 9AM to 5PM, and then disabled for the night. The user is not triggering the UI, so the code should request (politely) that the CommandManager requeries the state of the commands. This is done by calling the method InvalidateRequerySuggested on the CommandManager. And as you guessed, the method RaiseCanExecuteChanged of the RelayCommand class does just that."

+3  A: 

WPF doesn't update command bound controls unless it has a reason to. Clicking on the GUI causes WPF to refresh so the update then works.

You can manually cause a refresh of any command bound controls by calling CommandManager.InvalidateRequerySuggested.

Cameron MacFarland
Thanks, Cameron, I'll give this a shot. Is it lame to put this into a Timer with a reasonable Interval?
Dave
Probably. Can you just call it after you've finished processing?
Cameron MacFarland
I've seen the same behavior Dave is and executing CommandManager.InvalidateRequerySuggested did not help. I'm hoping someone here can provide some insight on why this sometimes happens.
Kilhoffer
Be very wary of CommandManager.InvalidateRequerySuggested. Take a look at a similar problem I posted a few months ago: http://stackoverflow.com/questions/1751966/commandmanager-invalidaterequerysuggested-isnt-fast-enough-what-can-i-do
unforgiven3
@unforgiven3: I'll give this a try. You should submit it as an answer. I hadn't come across your post when I was looking, but it was such a strange topic to search for.
Dave
I was hoping to find a better way to solve this problem, but in the end I just called `CommandManager.InvalidateRequerySuggested` in a slow-tick timer in the main thread, and this solved the problem for me. Thanks again for the tip!
Dave