15

The following events can be used, but, they must be attach for each element:

GotKeyboardFocus, LostKeyboardFocus

Is there a way in .NET WPF to globally detect if the focused element changed ? without having to add event listeners for all possible elements ?

simo
  • 23,342
  • 38
  • 121
  • 218

4 Answers4

21

You can do this in any class with this:

//In the constructor
EventManager.RegisterClassHandler(
        typeof(UIElement),
        Keyboard.PreviewGotKeyboardFocusEvent,
        (KeyboardFocusChangedEventHandler)OnPreviewGotKeyboardFocus);

...

private void OnPreviewGotKeyboardFocus(object sender, 
                                       KeyboardFocusChangedEventArgs e)
{

     // Your code here

}
Vaccano
  • 78,325
  • 149
  • 468
  • 850
  • Thanks. I select all the text of a textBox using this code in the handler: if (e.NewFocus is TextBoxBase textBox) textBox.SelectAll(); – steve Jun 07 '19 at 22:43
6

You can hook to the tunneling preview events:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="350" Width="525" 
    PreviewGotKeyboardFocus="Window_PreviewGotKeyboardFocus" 
    PreviewLostKeyboardFocus="Window_PreviewLostKeyboardFocus">
....

This way, as shown above, the window would be notified before all descendants when any of the descendants gets or loses the keyboard focus.

Read this for more information.

  • 1
    It seems like this method could lead to incorrect results due to the fact that it is a preview event, and therefore the focus change may not actually happen. This would occur if a UIElement between the window and the source element marked the event as handled. – Hank Feb 03 '14 at 20:26
  • I agree with @Hank. PreviewXxx doesn't guarantee it will actually change. Vaccano's answer is actually the correct one. Nicolas also has an alternate although it's not quite the same, as everyone in the chain gets notified thanks to the fall-through for handled events. – Mark A. Donohoe Nov 04 '15 at 16:24
  • 1
    @MarkA.Donohoe You say PreviewXXX doesn't guarantee, but you also say Vaccano's answer (which also uses PreviewXXX) is correct? – Kjara Feb 03 '21 at 11:58
  • I think you’re right… What it’s supposed to talk about is registering the class handler with the regular gotfocus event, not the previewgotfocus event. The key is the class handler not the regular event handler. – Mark A. Donohoe Feb 04 '21 at 16:14
5

You can add a routed event handler to your main window and specify you're interested in handled events.

mainWindow.AddHandler(
    UIElement.GotKeyboardFocusEvent,
    OnElementGotKeyboardFocus,
    true
);
Nicolas Repiquet
  • 9,097
  • 2
  • 31
  • 53
2

Have a look at how Microsoft trigger CommandManager.RequerySuggested event when focus changes: they subscribe to InputManager.PostProcessInput event.

ReferenceSource

Simple example:

static KeyboardControl()
{
    InputManager.Current.PostProcessInput += InputManager_PostProcessInput;
}

static void InputManager_PostProcessInput(object sender, ProcessInputEventArgs e)
{
    if (e.StagingItem.Input.RoutedEvent == Keyboard.GotKeyboardFocusEvent ||
        e.StagingItem.Input.RoutedEvent == Keyboard.LostKeyboardFocusEvent)
    {
        KeyboardFocusChangedEventArgs focusArgs = (KeyboardFocusChangedEventArgs)e.StagingItem.Input;
        KeyboardControl.IsOpen = focusArgs.NewFocus is TextBoxBase;
    }
}

This also works in multi-window applications.

nevermind
  • 2,300
  • 1
  • 20
  • 36
  • Wow! I like this a lot! It works for this scenario and would also have helped here: https://stackoverflow.com/questions/6489032/wpf-remove-focus-when-clicking-outside-of-a-textbox/36659144#36659144 - Weird that there's so little information to be found about this class... Also often probably a good alternative to `EventManager.RegisterClassHandler` – mike Feb 01 '23 at 11:29