apocryph.org Notes to my future self

10Sep/0613

WTF is wrong with WPF focus!?

There’s something badly broken about Windows Presentation Foundation focus. I’ve read that WPF implements its own concept of focus, and at the high level a WPF element tree is just a single HWND, and the WPF elements are rendered therein.

Here’s an experiment for you to try on your own: Create a simple XAML Window. Populate it with user controls. Now, try to get the first user control in the window to receive focus (keyboard and logical) when you’re window starts up. The FocusSample does it with a Button, which seems to work OK, but with a UserControl you’re out of luck. Even after setting Focusable to true, it doesn’t work. Yet, you can Tab through the focusable controls without problem.

First, you’ll try calling Keyboard.Focus() from within the Loaded event, but if you put trace output in the OnIsKeyboardFocusWithinChanged method of the user control, you’ll see that the call works, but it reversed again. Yes, it seems something about the window creation process after the Loaded event resets the keyboard focus, yet for some reason Button controls are impervious. TextBoxs may be as well; I’ve not tried.

In the end, I had to come up with a shameful hack. Behold, FocusHelper:

static class FocusHelper
{
    private delegate void MethodInvoker();

    public static void Focus(UIElement element)
    {
        //Focus in a callback to run on another thread, ensuring the main UI thread is initialized by the
        //time focus is set
        ThreadPool.QueueUserWorkItem(delegate(Object foo) {
            UIElement elem = (UIElement)foo;
            elem.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
                (MethodInvoker)delegate()
            {
                elem.Focus();
                Keyboard.Focus(elem);
            });
        }, element);
    }
}

Don’t get all holier-than-thou. War makes a man do things he’d never dream of doing otherwise. So does WPF programming.

Comments (13) Trackbacks (0)
  1. Agreed

    I completely agree, something is messed up with WPF focus. I found this snippet in the docs:

    “When setting initial focus at application startup, the element to receive focus must be connected to a PresentationSource and the element must have Focusable and IsVisible set to true. The recommended place to set initial focus is in the Loaded event handler. A Dispatcher callback can also be used by calling Invoke or BeginInvoke.”

    They give no further explanation as to how the element “must be connected to a PresentationSource,” or why a dispatcher callback would even be necessary.

    Anyhow, thanks for the example — they failed to give one of how to use a dispatcher for this.

    -Doug

  2. Worked for me

    I was trying to do this in a CAB environment to no avail. Your solution works. There’s got to be a better way . . .

  3. Consider adding a handler for the PreviewGotKeyboardFocused event onthe UserControl so it could delegate focus correctly.

        private void UserControl_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (e.OriginalSource == sender)
            {
                tb.Focus();
                e.Handled = true;
            }
        }
    

    In addition you’d also need to set Focusable to true on the UserControl.

    Lastly you can add a Loaded event handler to the Page and set the initial focus to the desired UserControl instance.

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            tb1.Focus();
        }
    
  4. Have you tried setting focus in the ContentRendered event instead of the Loaded event?

  5. using mentioned above using the Loaded events works well
    public IntercomPad()
    {
    InitializeComponent();
    Loaded += new RoutedEventHandler(IntercomPad_Loaded);
    }

    void IntercomPad_Loaded(object sender, RoutedEventArgs e)
    {
    //FocusManager.SetFocusedElement(this, tPhoneNumber);
    IInputElement ie = Keyboard.Focus(tPhoneNumber);
    }

  6. Yeah, WPF is messed up. But thank you … over a year later… your snippet saved me hours of hassle!

  7. I didn’t understand why I set Focus() inside LostFocus event is ended with stack overflow. Your post saves alot of my works.

  8. This works too:

    ///
    /// Focuses the specified UI element.
    ///
    /// The UI element.
    public static void DelayedFocus ( this UIElement uiElement )
    {
    uiElement.Dispatcher.BeginInvoke(
    new Action(delegate
    {
    uiElement.Focusable = true;
    uiElement.Focus();
    Keyboard.Focus(uiElement);
    }),
    DispatcherPriority.Render);
    }

  9. had similar feeling the focus is messed in wpf. tried some stuff. as you can read here: http://www.batteryslave.com/2009/05/what-is-wrong-with-focus-in-wpf/
    i added some testcode. some stuff from above worked half. but not in the examples in my article…too bad

    batteryslave

  10. DontFocusTooEarly is right – create a method
    “private void Content_Rendered(object sender, EventArgs e)”
    and in the XAML tag, put:
    ContentRendered=”Content_Rendered”

    then in the Content_Rendered method you have just created, use Keyboard.Focus(object); , or whatever else you want to do with focus.

  11. hi can you explain us how to use it in the XAML

  12. Thanks for the quick solution–not elegant but it works and sometimes that’s what counts when it comes to WPF. :P

  13. Your last three lines made my day a little less painful. Thank you for that.


Leave a comment


No trackbacks yet.

Delicious Bookmarks

Recent Posts

Meta

Current Location