Hi,
When placing a RichTextBox inside a ContentPane, the functionality selecting a text typed inside the RichTextBox works fine. But when right clicking on the selected text and invoking the context menu the selection on the text gets unselected. And again after the context menu is dismissed the text selection that was lost gets selected again.
This is not the behavior when placing the RichTextBox on a StackPanel or a Grid.
Is this an intended behavior or a bug in ContentPane. Is there a workaround to resolve this issue.
-Alex
Yes I believe that logic happens when the focused element can no longer be focused. Then WPF does what I feel is a bug. It basically assumes that if the element is not part of the active PresentationSource - it will focus the root visual of the active PresentationSource. However it does not consider the fact that the old element might have been in a Popup which cannot be the active PresentationSource (because those top level windows don't get activated). You probably need to track down what element had focus and make sure to shift focus to another element before that element is no longer focusable. I've seen this come up for example in some controls where you are in edit mode and an element in that edit template had keyboard focus but can no longer be focused when the template is changed (because it is out of the visual tree). Focus in that case really should have been shifted out of the edit template before removing/clearing the edit template.
No apologies needed. With regards to your issue, it sounds similar to what the button does. Technically it isn't directly focusing the root focus scope's FocusedElement. What it does is call Keyboard.Focus(null). The framework's interpretation of that is to get the "active" PresentationSource (HwndSource created for each Window/Popup although the popup won't be considered active) and then get its RootVisual and try to shift focus to that. The default implementation for the FrameworkElement's is to shift focus to its FocusedElement if it is a focus scope. The xamDockManager tries to intercept this process by catching the PreviewLostKeyboardFocus (or possibly the PreviewGotKeyboardFocus if the other isn't raised) and mark it handled. Unless the WPF framework has been told to force the focus shift or if the element losing focus is no longer focusable (e.g. its disabled, etc) then it might proceed with the focus shift anyway. I'm not sure what is happening in your original sample but you might try handling the GotKeyboardFocus of the element that is erroneously receiving the focus and see what element had focus (i.e. e.OldFocus) and why it might be considered no longer focusable (e.g. is it disabled, etc.). If you can narrow it down so we can see the issue here then we might be able to do something in the xamDockManager depending on why it is happening.
FYI....after some more debugging, it appears that the code that is stealing focus is in KeyboardDevice, where it does Focus(null). Now I'm just trying to wrap my head around how/if this relates to the Focus Scope issue.
private
bool
CheckForDisconnectedFocus()
{
wasDisconnected =
false
;
if
(InputElement.GetRootVisual (_focus
as
DependencyObject) != _focusRootVisual)
true
Focus (
null
);
}
return
wasDisconnected;
Andrew,
Thank you for your detailed reply, and I owe you an apology. My recollection on the MS documentation was incorrect. The things I read were via MSDN, but they were blogs and forum posts rather than official documentation. Looking again I was unable to find anything official pointing IsFocusScope to be used for "temporarily" borrowing focus. I apologize for that.
After playing around more with my particular issue, I guess I'm uncertain if ContentPane being a FocusScope plays a role or not. The most interesting part of your response to me was your last paragraph where you mentioned certain controls "booting" focus to a parent scope. This is the behavior that I experience (focus is booted from a DataGridCell within the ContentPane out to the Main application window. Nothing has logical focus within the parent scope, so I think this makes sense that focus goes to the window itself. The only oddity is that the thing that causes focus to shift is a command that is triggered via an input key binding, so I wouldn't think any buttons should be losing focus in this process. I've been trying to reproduce this in a sample app with/without the DockManager, but have been unsuccessful thus far. Any chance you've run into something like this in the past?Again, thanks for your response. I've read some of your other writing regarding WPF and I respect your expertise on the subject.
-Luke
I don't recall (certainly not 6 years ago when the control was first written but even now) MS' documentation stating that this is for temporary focus - after all it is set to true on a Window. The documentation (IsFocusScope and Input Overview) basically describes the property as being a means for making an element a container that tracks the focused element within that element. Even when keyboard focus is no longer within that element, the FocusedElement is still considered logically focused. This is basically why the property was made true for ContentPanes - because each pane is really an isolated group of elements where the focus for each should be maintained separately. One could make arguments for why it should or should not be true in this case but I won't get into that. Instead I'll just mention the implications of having this true or not (at least the ones I know since this property is used/referenced internally by some MS controls and assumptions made based on that) and you can decide whether you want to change it to false.
So having IsFocusScope set to true will mean that the FocusedElement of the FocusScope (the ContentPane in this case) will be used to store a reference to the logically focused element within that scope. So when a pane is re-activated, focus will be shifted back to that FocusedElement (basically the last element within that had keyboard focus). In addition, the Got/LostFocus events are based upon logical focus. So if an element within a pane has keyboard focus and you shift focus to another focusscope (i.e. another pane, another window, the content of the xamdockmanager, etc.) that element's LostFocus events will not be triggered and if one had a binding on that element with an UpdateSourceTrigger of LostFocus (the TextBox's Text property does this by default) then the binding will not be updated either. That is until you shift focus to another element within that focusscope (again the pane in this case). So setting IsFocusScope to false would mean that shifting focus to any other element in that window will cause it to lose logical focus and its LostFocus event will fire and any LostFocus UpdateSourceTrigger bindings will update the source. Notice I said within that window because shifting focus to an element in another window (even if that is a floating pane since in a standard wpf application that is hosted within a window) that element will still be the logically focused element since it will be the FocusedElement of the Window.
The focus scope also has an impact on the routing of RoutedCommands. When routing the CanExecute and Execute events of a RoutedCommand, the WPF CommandManager checks if an element is a focus scope. If it is then it will re-route the events through the FocusedElement of that focus scope. So if for example you had a Toolbar within a ContentPane and you had some RoutedCommands associated with the buttons in that toolbar, the CanExecute/Execute would (by default) route up the visual tree until it hits the Toolbar. The CommandManager would then see that the Toolbar is a focus scope and it would get the containing focus scope and get the focused element of that and start routing the CanExecute/Execute through that element. So if the IsFocusScope of the pane is true that will always route through the last focused element within that pane. If IsFocusScope were false then the parent focus scope would likely be the window and it would route to whatever is the logically focused element there - which may be in that pane, some other pane or some other element within the window.
Unfortunately there are also a number of elements within the framework that make certain assumptions based on the FocusScope. For example a ButtonBase assumes that if it is not in the root focus scope (i.e. if the visual parent of the button's focus scope is non-null) that it should shift keyboard focus out of the focus scope when the button is clicked (if it has keyboard focus when it loses capture anyway). The ContentPane works around this assumption. Also, elements like Selector (e.g. ListBox), TextBoxBase and TreeView assume that if they lose keyboard focus and the element that received keyboard focus is in not the focused element of the root focus scope (e.g. the Window or ElementHost) that they should continue to display their selection as if active. This was done to try and keep the selection showing as active (e.g. blue) when you dropped down a menu, etc. It would have been better if MS has another property so an element could indicate it should be considered "root" focus scope in which case both of these issues could have been avoided.