11
votes

In a WPF program I want to get the current (keyboard) Focus, store it and re-set it later.

To get the current focus right now I use:

DependencyObject focusScope = FocusManager.GetFocusScope(d);
_lastFocus = FocusManager.GetFocusedElement(focusScope);

To set it later I use:

if (_lastFocus != null)
{
  IInputElement setFocus = _lastFocus;
  _lastFocus = null;
  d.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new ThreadStart(delegate
  {
    FocusManager.SetFocusedElement(d, setFocus);
  }));
}

This works once. But if I try again, it does not work until I re-create the offending dialog which I try to set the focus to, even though it does the very same thing (I traced getting and setting the Focus). Instead the main window itself gets the focus.

I once heard there are two kinds of focus in WPF, do I need to set something else, too, to get consistent results?

2

2 Answers

34
votes

You are correct. WPF has logical focus, which you are using there and keyboard focus. You can find full details of the two in the Focus Overview page of MSDN. From that page:

Keyboard Focus

Keyboard focus refers to the element that is currently receiving keyboard input. There can be only one element on the whole desktop that has keyboard focus. In WPF, the element that has keyboard focus will have IsKeyboardFocused set to true. The static property FocusedElement on the Keyboard class gets the element that currently has keyboard focus.

In order for an element to obtain keyboard focus, the Focusable and the IsVisible properties on the base elements must be set to true. Some classes, such as the Panel base class, have Focusable set to false by default; therefore, you must set Focusable to true if you want such an element to be able to obtain keyboard focus.

Keyboard focus can be obtained through user interaction with the UI, such as tabbing to an element or clicking the mouse on certain elements. Keyboard focus can also be obtained programmatically by using the Focus method on the Keyboard class. The Focus method attempts to give the specified element keyboard focus. The returned element is the element that has keyboard focus, which might be a different element than requested if either the old or new focus object block the request.

Logical Focus

Logical focus refers to the FocusManager.FocusedElement in a focus scope. A focus scope is an element that keeps track of the FocusedElement within its scope. When keyboard focus leaves a focus scope, the focused element will lose keyboard focus but will retain logical focus. When keyboard focus returns to the focus scope, the focused element will obtain keyboard focus. This allows for keyboard focus to be changed between multiple focus scopes but ensures that the focused element in the focus scope regains keyboard focus when focus returns to the focus scope.

There can be multiple elements that have logical focus in an application, but there may only be one element that has logical focus in a particular focus scope.

An element that has keyboard focus has logical focus for the focus scope it belongs to.


Coming back to your question, the other kind of focus that you are not using is Keyboard.Focus. You can use it like this:

Keyboard.Focus(theButtonThatYouWantToFocus);

Also, note that the UIElement.Focus() method will attempt to set both logical and keyboard focus to the element that it was called on. It will return true if keyboard focus and logical focus were set to this element and false if only logical focus was set to this element.


One other method that you can use to focus controls is to use the FocusManager.FocusedElement Attached Property. Most people use this statically and in this case, this will work just once when the view loads:

<Grid FocusManager.FocusedElement="{Binding ElementName=TextBoxToFocus}">
    <TextBox Name="TextBoxToFocus" Text="Focus Me" />
<Grid>

However, it is possible to use this in a DataTrigger and to set it dependent on a custom bool property, in this example, the IsFocused property:

<Style x:Key="FocusableTextBoxStyle" TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsFocused}" Value="True">
            <Setter Property="FocusManager.FocusedElement" 
                Value="{Binding RelativeSource={RelativeSource Self}}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

So whenever I set the IsFocused property to true from the view model, any element with this Style applied will get logical focus. Now clearly, this Style is for the TextBox control, but it will still work if you change it to Control for instance.

0
votes

First, you should use Keyboard.ClearFocus() and write Keyboard.Focus("Your Input element") later.

Keyboard.ClearFocus() clear your focus reference. After, you can do focus to whatever element of your interface.