I have a RoutedUICommand
command which can be fired in two different ways:
ICommand.Execute
upon a button click event;>
Okay, I'll try to describe the issue, as I understand it. Let's start with a quote from the MSDN section with FAQ (Why are WPF commands not used?
):
Additionally, the command handler that the routed event is delivered to is determined by the current focus in the UI. This works fine if the command handler is at the window level, because the window is always in the focus tree of the currently focused element, so it gets called for command messages. However, it does not work for child views who have their own command handlers unless they have the focus at the time. Finally, only one command handler is ever consulted with routed commands.
Please pay attention to the line:
who have their own command handlers unless they have the focus at the time.
It is clear that when the focus is not, the command will not be executed. Now the question is: what is the documentation mean focus? This refers to the type of focus? I remind there are two types of focus: logical and keyboard focus.
Now let a quote from here:
The element within the Windows focus scope that has logical focus will be used as the command target.
Note
that it's the windows focus scope not the active focus scope. And it's logical focus not keyboard focus. When it comes to command routing FocusScopes remove any item you place them on and it's child elements from the command routing path. So if you create a focus scope in your app and want a command to route in to it you will have to set the command target manually. Or, you can not use FocusScopes other than for toolbars, menus etc and handle the container focus problem manually.
According to these sources, it is possible to assume that the focus must be active, i.e. an element that can be used with keyboard focus, for example: TextBox
.
To further investigate, I am a little changed your example (XAML section):
I added the command in StackPanel
and added Menu
control. Now, if you click to clear focus, controls associated with the command, will not be available:
Now, if we click on the button Test (ICommand.Execute)
we see the following:
Keyboard focus is set on the Window
, but the command still does not run. Once again, remember the note, the above:
Note that it's the windows focus scope not the active focus scope.
He does not have an active focus, so the command does not work. It will only work if the focus is active, set to TextBox
:
Let's go back to your original example.
Clearly, the first Button
does not cause the command, without the active focus. The only difference is that in this case, the second button is not disabled because there is no active focus, so clicking on it, we call the command directly. Perhaps, this is explained by a string of MSDN
quotes:
This works fine if the command handler is at the window level, because the window is always in the focus tree of the currently focused element, so it gets called for command messages.
I think, I found another source that should explain this strange behavior. Quote from here:
Menu items or toolbar buttons are by default placed within a separate FocusScope (for the menu or toolbar respectively). If any such items trigger routed commands, and they do not have a command target already set, then WPF always looks for a command target by searching the element that has keyboard focus within the containing window (i.e. the next higher-up focus scope).
So WPF does NOT simply look up the command bindings of the containing window, as you'd intuitively expect, but rather always looks for a keyboard-focused element to set as the current command target! Apparently the WPF team took the quickest route here to make built-in commands such as Copy/Cut/Paste work with windows that contain multiple text boxes or the like; unfortunately they broke every other command along the way.
And here's why: if the focused element within the containing window cannot receive keyboard focus (say, it's a non-interactive image), then ALL menu items and toolbar buttons are disabled -- even if they don't require any command target to execute! The CanExecute handler of such commands is simply ignored.
Apparently the only workaround for problem #2 is to explicitly set the CommandTarget of any such menu items or toolbar buttons to the containing window (or some other control).