213

It seems that when a WPF application starts, nothing has focus.

This is really weird. Every other framework I've used does just what you'd expect: puts initial focus on the first control in the tab order. But I've confirmed that it's WPF, not just my app -- if I create a new Window, and just put a TextBox in it, and run the app, the TextBox doesn't have focus until I click on it or press Tab. Yuck.

My actual app is more complicated than just a TextBox. I have several layers of UserControls within UserControls. One of those UserControls has Focusable="True" and KeyDown/KeyUp handlers, and I want it to have the focus as soon as my window opens. I'm still somewhat of a WPF novice, though, and I'm not having much luck figuring out how to do this.

If I start my app and press the Tab key, then focus goes to my focusable control, and it starts working the way I want. But I don't want my users to have to hit Tab before they can start using the window.

I've played around with FocusManager.FocusedElement, but I'm not sure which control to set it on (the top-level Window? the parent that contains the focusable control? the focusable control itself?) or what to set it to.

What do I need to do to get my deeply-nested control to have initial focus as soon as the window opens? Or better yet, to focus the first focusable control in the tab order?

Joe White
  • 94,807
  • 60
  • 220
  • 330

12 Answers12

192

This works, too:

<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}">

   <DataGrid x:Name="SomeElement">
     ...
   </DataGrid>
</Window>
George
  • 2,451
  • 27
  • 37
Sean
  • 1,929
  • 1
  • 11
  • 2
  • 4
    I'm surprised I'm the first person who commented on this. I was confused as to where this went because it could go on almost any control. In answer to this specific question, I think it would go on the window, but you can read the remarks on http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager%28v=vs.110%29.aspx to understand how the control you attach this to matters. – Joel McBeth Jul 01 '14 at 23:07
  • I have used this approach on a stackpanel with success. If one is interested, there is an example at http://stackoverflow.com/a/2872306/378115 – Julio Nobre Dec 17 '16 at 00:35
  • 1
    This worked for me much better than accepted answer because I need to put focus on element that is after first. – Puterdo Borato Aug 01 '17 at 12:28
176

I had the bright idea to dig through Reflector to see where the Focusable property is used, and found my way to this solution. I just need to add the following code to my Window's constructor:

Loaded += (sender, e) =>
    MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

This will automatically select the first control in the tab order, so it's a general solution that should be able to be dropped into any window and Just Work.

Joe White
  • 94,807
  • 60
  • 220
  • 330
  • 21
    Add turn that into a behavior. ... – wekempf May 08 '09 at 16:08
  • 6
    @wekempf, I wasn't familiar with the idea of behaviors, but I looked into it and that's not a bad idea at all. If anyone else (like me) isn't already familiar with attached behaviors, here's an explanation: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx – Joe White May 09 '09 at 13:43
  • 1
    Additionally, this works if the desired element is a UserControl which contains the actual focusable element (even in deep hierarchies). Great! – Daniel Albuschat May 21 '15 at 10:08
  • 1
    Great idea, but sometimes it doesn't work if the control that would accept focus is a `Button`. To fix this, I flip the `MoveFocus` call over the dispatcher at `ContextIdle` priority (`Background` or higher does not work). Also, there is a `FocusNavigationDirection.First` that matches the intent better and does the same thing in this case. – Anton Tykhyy Apr 15 '17 at 08:18
  • that should be the default behavior! Yuck (in original post) is right! – NH. May 30 '17 at 23:47
  • If you put this inside a constructor as is, you will not be able to unsubscribe the anonymous method from the `Loaded` event. If the window in question is opened many times consecutively, this creates a small, but persistent, memory leak. – Nikola Novak Jul 20 '19 at 08:31
  • @NikolaNovak True, this assumes you're following the typical pattern of creating a new Window instance each time. If you wanted to use the same Window instance multiple times, you'd hit other problems: since this pretends to hit Tab every time the window is shown, you'd end up cycling through a different focused control each time you showed the window! As always, if you do advanced things, simple solutions may no longer work. – Joe White Jul 21 '19 at 21:03
  • As @AntonTykhyy [pointed out](https://stackoverflow.com/q/817610#comment73906145_818536), this solution should definitely use `FocusNavigationDirection.First` instead of `FocusNavigationDirection.Next`. Specifying `Next` wanders out of the specified `FocusScope` and down into another if the specified `FocusScope` has no focusable element within it. This makes matters worse if you're (e.g.) trying to set up an initial `FocusedElement` for each `FocusScope` in your app. Using `First` makes `MoveFocus` return false; the scope's `FocusedElement` remains `null` rather than focusing a random element – Glenn Slayden Nov 30 '19 at 17:12
  • @GlennSlayden I've never wrapped my head properly around focus scopes, but using First does seem like a good general plan. I updated my answer. Thanks! – Joe White Dec 03 '19 at 22:49
68

Based on the accepted answer implemented as an attached behavior:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace UI.Behaviors
{
    public static class FocusBehavior
    {
        public static readonly DependencyProperty FocusFirstProperty =
            DependencyProperty.RegisterAttached(
                "FocusFirst",
                typeof(bool),
                typeof(FocusBehavior),
                new PropertyMetadata(false, OnFocusFirstPropertyChanged));

        public static bool GetFocusFirst(Control control)
        {
            return (bool)control.GetValue(FocusFirstProperty);
        }

        public static void SetFocusFirst (Control control, bool value)
        {
            control.SetValue(FocusFirstProperty, value);
        }

        static void OnFocusFirstPropertyChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            Control control = obj as Control;
            if (control == null || !(args.NewValue is bool))
            {
                return;
            }

            if ((bool)args.NewValue)
            {
                control.Loaded += (sender, e) =>
                    control.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
            }
        }
    }
}

Use it like this:

<Window xmlns:Behaviors="clr-namespace:UI.Behaviors"
        Behaviors:FocusBehavior.FocusFirst="true">
Community
  • 1
  • 1
Mizipzor
  • 51,151
  • 22
  • 97
  • 138
  • 6
    In my opinion, this is *by far* the best solution I have found. Thanks! – Shion Mar 21 '14 at 14:23
  • 1
    There is a bug in the code in this answer in the call to `DependencyProperty.RegisterAttached`. The third parameter should be `typeof(FocusBehavior)`, not `typeof(Control)`. Making this change will prevent the designer from reporting 'FocusFirst' property already registered by 'Control' errors. – Tony Vitabile Mar 10 '16 at 18:58
  • @TonyVitabile Fixed. You are always free to edit and improve answers if you can. :) – Mizipzor Apr 01 '16 at 14:13
  • 1
    Shouldn't control.Loaded event handler be deregistered during unload? – andreapier Sep 21 '16 at 14:58
  • @andreapier You could if you cared, but skipping the deregister wouldn't cause a memory leak or anything. You only need to worry about events causing memory leaks if a short-lived object has a method being attached to an event on a long-lived object. In this case, the lifetime is that of the window, so you're fine. – Joe White Oct 20 '16 at 15:52
14

I found another possible solution. Mark Smith posted a FirstFocusedElement markup extension for use with FocusManager.FocusedElement.

<UserControl x:Class="FocusTest.Page2"
    xmlns:FocusTest="clr-namespace:FocusTest"
    FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
Joe White
  • 94,807
  • 60
  • 220
  • 330
9

Had same problem solved it with simple solution: In the main window:

  <Window ....
        FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}"
         ... />

In the user control:

private void UserControl_GotFocus_1(object sender, RoutedEventArgs e)
        {
            targetcontrol.Focus();
            this.GotFocus -= UserControl_GotFocus_1;  // to set focus only once
        }
Vladik Y
  • 411
  • 5
  • 6
9

You can easily have the control set itself as the focused element in XAML.

<Window>
   <DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">
     ...
   </DataGrid>
</Window>

I've never tried setting this in a usercontrol and seeing if this works, but it may.

Simon Gillbee
  • 3,932
  • 4
  • 35
  • 48
  • It sounds interesting, because you don't have do name the control just for a focus issue. In the other hand, my test with user control did not work. – heringer Oct 09 '15 at 16:23
  • That doesn't surprise me @heringer... that would be like trying to set focus on a or similar non-interactive control. You could try applying this FocusedElement attribute on an interactive control inside the usercontrol. But that may not be an option. – Simon Gillbee Oct 11 '15 at 02:06
  • I have used this approach on a stackpanel to set which child button I wanted to focus once the form is loaded. Thanks a lot – Julio Nobre Dec 17 '16 at 00:32
  • Be careful, it may make bindings totally broken. https://stackoverflow.com/questions/30676863/wpf-data-bindings-being-broken-by-focusmanager-why – Der_Meister Feb 06 '18 at 11:53
9

After having a 'WPF Initial Focus Nightmare' and based on some answers on stack, the following proved for me to be the best solution.

First, add your App.xaml OnStartup() the followings:

EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
          new RoutedEventHandler(WindowLoaded));

Then add the 'WindowLoaded' event also in App.xaml :

void WindowLoaded(object sender, RoutedEventArgs e)
    {
        var window = e.Source as Window;
        System.Threading.Thread.Sleep(100);
        window.Dispatcher.Invoke(
        new Action(() =>
        {
            window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

        }));
    }

The threading issue must be use as WPF initial focus mostly fails due to some framework race conditions.

I found the following solution best as it is used globally for the whole app.

Hope it helps...

Oran

OrPaz
  • 1,065
  • 11
  • 25
3

A minimal version of Mizipzor's answer for C# 6+.

public static class FocusBehavior
{
    public static readonly DependencyProperty GiveInitialFocusProperty =
        DependencyProperty.RegisterAttached(
            "GiveInitialFocus",
            typeof(bool),
            typeof(FocusBehavior),
            new PropertyMetadata(false, OnFocusFirstPropertyChanged));

    public static bool GetGiveInitialFocus(Control control) => (bool)control.GetValue(GiveInitialFocusProperty);
    public static void SetGiveInitialFocus(Control control, bool value) => control.SetValue(GiveInitialFocusProperty, value);

    private static void OnFocusFirstPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var control = obj as Control;

        if (control == null || !(args.NewValue is bool))
            return;

        if ((bool)args.NewValue)
            control.Loaded += OnControlLoaded;
        else
            control.Loaded -= OnControlLoaded;
    }

    private static void OnControlLoaded(object sender, RoutedEventArgs e) => ((Control)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}

Use in your XAML:

<Window local:FocusBehavior.GiveInitialFocus="True" />
Community
  • 1
  • 1
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
2

Above solution was not working as expected for me, I've changed slightly the behavior proposed by Mizipzor as following:

From this part

if ((bool)args.NewValue)
        {
            control.Loaded += (sender, e) =>
                   control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }

To this

if ((bool)args.NewValue)
        {
            control.Loaded += (sender, e) => control.Focus();
        }

ANd I'm not attaching this behavior to Window or UserControl, but to control I want to focus initially, e.g.:

<TextBox ui:FocusBehavior.InitialFocus="True" />

Oh, sorry for different naming I'm using InitialFocus name for the attached property.

And this is working for me, maybe it could help someone else.

BrightShadow
  • 415
  • 4
  • 6
1

If you are like me, and you are using some frameworks that, somehow, mess up with the basic focus behaviors, and make all solutions above irrelevant, you can still do this :

1 - Note the element which get the focus (whatever it is!)

2 - Add this in your code behind xxx.xaml.cs

private bool _firstLoad;

3 - Add this on the element which get the first focus :

GotFocus="Element_GotFocus"

4 - Add the Element_GotFocus method in the code behind, and specify the WPF named element who need the first focus :

private void Element_GotFocus(object sender, RoutedEventArgs e)
{
    if(_firstLoad)
    {
        this.MyElementWithFistFocus.Focus();
        _firstLoad = false;
    }
}

5 - Manage the Loaded event

in XAML

Loaded="MyWindow_Loaded"   

in xaml.cs

private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
        _firstLoad = true;
        this.Element_GotFocus(null, null);
}

Hope this will help as a last resort solution

G.Dealmeida
  • 325
  • 3
  • 14
0

I also faced the same problem. I had three text boxes inside canvas container and wanted the first text box to be focused when the user control opens. WPF code followed MVVM pattern. I created a separate behavior class for focusing the element and binded it to my view like this.

Canvas behavior code

public  class CanvasLoadedBehavior : Behavior<Canvas>
{
    private Canvas _canvas;
    protected override void OnAttached()
    {
        base.OnAttached();
        _canvas = AssociatedObject as Canvas;
        if (_canvas.Name == "ReturnRefundCanvas")
        {

            _canvas.Loaded += _canvas_Loaded;
        }


    }

    void _canvas_Loaded(object sender, RoutedEventArgs e)
    {
        FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;

        // MoveFocus takes a TraveralReqest as its argument.
        TraversalRequest request = new TraversalRequest(focusDirection);
        UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
        if (elementWithFocus != null)
        {
            elementWithFocus.MoveFocus(request);
        }

    }

}

Code for view

<Canvas  Name="ReturnRefundCanvas" Height="200" Width="1466" DataContext="{Binding RefundSearchViewModel}">
                <i:Interaction.Behaviors>
                    <b:CanvasLoadedBehavior />
                </i:Interaction.Behaviors>
                <uc:Keyboard Canvas.Left="973" Canvas.Top="111" ToolTip="Keyboard" RenderTransformOrigin="-2.795,9.787"></uc:Keyboard>
                <Label  Style="{StaticResource Devlbl}" Canvas.Left="28" Content="Return and Refund Search" Canvas.Top="10" />
                <Image Height="30" Width="28" Canvas.Top="6" Canvas.Left="5" Source="pack://application:,,,/HomaKiosk;component/images/searchF.png">
                    <Image.OpacityMask>
                        <ImageBrush ImageSource="pack://application:,,,/HomaKiosk;component/images/searchF.png"/>
                    </Image.OpacityMask>
                </Image>

                <Separator Height="4" Canvas.Left="6" Margin="0" Canvas.Top="35" Width="1007"/>

                <ContentControl Canvas.Top="45" Canvas.Left="21"
                    ContentTemplate="{StaticResource ErrorMsg}"
                    Visibility="{Binding Error, Converter={c:StringNullOrEmptyToVisibilityConverter}}" 
                    Content="{Binding Error}" Width="992"></ContentControl>

                <Label  Style="{StaticResource Devlbl}" Canvas.Left="29" Name="FirstName" Content="First Name" Canvas.Top="90" />
                <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" Canvas.Left="33" ToolTip="First Name"  Canvas.Top="120" Width="205"                     Padding="10,5" TabIndex="1001"
                    VerticalAlignment="Top"

                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"

                    Text="{Binding FirstName, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding FirstNameSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical">
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold" />
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                </wpf:AutoCompleteTextBox>

                <Label Style="{StaticResource Devlbl}" Canvas.Left="250" Content="Last Name" Canvas.Top="90" />
                <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" ToolTip="Last Name" Canvas.Left="250"  Canvas.Top="120" Width="205" Padding="10,5"  TabIndex="1002"
                    VerticalAlignment="Top"
                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"
                   Text="{Binding LastName, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding LastNameSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical">
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold" />
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                </wpf:AutoCompleteTextBox>

                <Label Style="{StaticResource Devlbl}" Canvas.Left="480" Content="Receipt No" Canvas.Top="90" />
                             <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" ToolTip="Receipt No" Canvas.Left="480"  Canvas.Top="120" Width="205" Padding="10,5"  TabIndex="1002"
                    VerticalAlignment="Top"
                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"
                    Text="{Binding ReceiptNo, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding ReceiptIdSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical" >
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold">

                                    </TextBlock>
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                    <i:Interaction.Behaviors>
                        <b:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]+$" MaxLength="15" />
                    </i:Interaction.Behaviors>
                </wpf:AutoCompleteTextBox>
                <!--<Label Style="{StaticResource Devlbl}" Canvas.Left="710" Content="Duration" Canvas.Top="79" />-->
                <!--<ComboBox AllowDrop="True" Canvas.Left="710" ToolTip="Duration" Canvas.Top="107" Width="205" TabIndex="1004"
                    Style="{StaticResource CommonComboBox}"      
                    ItemsSource="{Binding Durations}" DisplayMemberPath="Description" SelectedValuePath="Id" SelectedValue="{Binding SelectedDate, Mode=TwoWay}">

                </ComboBox>-->

                <Button Content="Search" Style="{StaticResource MyButton}" ToolTip="Search" 
                    Canvas.Top="116" Canvas.Left="710" Cursor="Hand" 
                    Command="{Binding SearchCommand}" TabIndex="2001">
                </Button>
                <Button Content="Clear" Style="{StaticResource MyButton}"  ToolTip="Clear"
                    Canvas.Top="116" Canvas.Left="840" Cursor="Hand" 
                    Command="{Binding ClearCommand}" TabIndex="2002">
                </Button>
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="25" Source="pack://application:,,,/HomaKiosk;component/images/chkpending.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="50" Content="Check Returned and Payment Pending" Canvas.Top="178" />
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="300" Source="pack://application:,,,/HomaKiosk;component/images/chkrepaid.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="325" Content="Repaid" Canvas.Top="178" />
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="395" Source="pack://application:,,,/HomaKiosk;component/images/refund.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="415" Content="Refunded" Canvas.Top="178" />
                 </Canvas>
BSG
  • 2,084
  • 14
  • 19
-1
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">
dnk.nitro
  • 496
  • 6
  • 13