I recently hit a problem when attempting to focus a control in WPF. Anyone who has looked into the focus model within WPF will know that there are three distinct ways of programmatically setting focus on an IInputElement. If you are interested in learning about how WPF handles element focus take a look at the MSDN. For a quick review here are the three mechanisms for setting focus in code.
Keyboard.Focus()
WPF defines keyboard focus as the element that is currently accepting keyboard input. There can only ever be one element on the entire desktop that has keyboard focus. To manage keyboard focus we use the Keyboard class.
Keyboard.Focus(TextBox1);
It makes sense that keyboard focus can only be applied to elements that are visible and that some elements should not be able to receive focus, usually container elements. Therefore Focus() will not succeed if either IsVisible or IsFocusable for the target element is false.
FocusManager.SetFocusedElement()
As user interfaces are become more contextual it is useful to be able to control focus behaviour within the logical regions of a user interface. For example, how can I control the element that will receive focus when a given user interface fragment becomes active? This is what WPF describes as Logical Focus. UI elements, usually container elements, are defined as being a Focus Scope, and within a given focus scope it is possible to specify an element that has logical focus. When the focus scope becomes active the element having logical focus gets keyboard focus. To specify logical focus we use the FocusManager class.
FocusManager.SetFocusedElement(focusScope, TextBox1);
UIElement.Focus()
If you are new to WPF and you want to set focus on an element the chances are you will come across this conveniently located method. This method first attempts to set keyboard focus on the target element, failing that will attempt to set logical focus.
Back to the Problem
I have a UserControl containing a number of UIElements. The user control is dynamically loaded into a ContentControl and I want to specify which UIElement is to receive focus before the UserControl is loaded. My UserControl may be replaced by other content depending on the actions of the user.
I simply create the UserControl and call UIElement.Focus() on my required element. Of course my control will not get keyboard focus as it is not yet visible so instead it will receive logical focus within the focus scope that is my UserControl.
This all works beautifully until you come across the situation where the logical focus for the UserControl has already been set. For example, what if reuse my UserControl instance and it already contains an element that has logical focus?
Mysteriously, UIElement.Focus() is simply ignored and focus remains with the element that previously had logical focus.
The reason for this is apparent if you look at what UIElement.Focus() is actually doing.
...
DependencyObject focusScope = FocusManager.GetFocusScope(this);
if (FocusManager.GetFocusedElement(focusScope) == null)
{
FocusManager.SetFocusedElement(focusScope, this);
}
...
So UIElement.Focus will only succeed if logical focus has not already been set.
Not only can we see the problem but we are presented with a simple solution. Use FocusManager to set logical scope if you know you want logical scope.
I cannot see any benefit for UIElement.Focus allowing logical focus within a focus scope to be set only once. I would be interested to hear any ideas as to why it should.