WPF and initial focus
Asked Answered
S

12

215

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?

Silassilastic answered 3/5, 2009 at 17:59 Comment(0)
S
177

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.

Silassilastic answered 4/5, 2009 at 1:46 Comment(9)
Add turn that into a behavior. <Window FocusBehavior.FocusFirst="true"> ... </Window>Schlueter
@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: codeproject.com/KB/WPF/AttachedBehaviors.aspxSilassilastic
Additionally, this works if the desired element is a UserControl which contains the actual focusable element (even in deep hierarchies). Great!Auxochrome
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.Rodroda
that should be the default behavior! Yuck (in original post) is right!Souza
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.Balzac
@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.Silassilastic
As @AntonTykhyy pointed out, 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 elementAleppo
@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!Silassilastic
H
197

This works, too:

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

   <DataGrid x:Name="SomeElement">
     ...
   </DataGrid>
</Window>
Heracles answered 27/8, 2009 at 21:58 Comment(3)
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 msdn.microsoft.com/en-us/library/… to understand how the control you attach this to matters.Neutrality
I have used this approach on a stackpanel with success. If one is interested, there is an example at https://mcmap.net/q/128465/-set-the-focus-on-a-textbox-in-wpf-xamlRickety
This worked for me much better than accepted answer because I need to put focus on element that is after first.Cardwell
S
177

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.

Silassilastic answered 4/5, 2009 at 1:46 Comment(9)
Add turn that into a behavior. <Window FocusBehavior.FocusFirst="true"> ... </Window>Schlueter
@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: codeproject.com/KB/WPF/AttachedBehaviors.aspxSilassilastic
Additionally, this works if the desired element is a UserControl which contains the actual focusable element (even in deep hierarchies). Great!Auxochrome
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.Rodroda
that should be the default behavior! Yuck (in original post) is right!Souza
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.Balzac
@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.Silassilastic
As @AntonTykhyy pointed out, 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 elementAleppo
@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!Silassilastic
S
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">
Sites answered 23/1, 2012 at 15:51 Comment(5)
In my opinion, this is by far the best solution I have found. Thanks!Zela
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.Bacciferous
@TonyVitabile Fixed. You are always free to edit and improve answers if you can. :)Sites
Shouldn't control.Loaded event handler be deregistered during unload?Leatri
@Leatri 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.Silassilastic
S
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}">
Silassilastic answered 10/5, 2009 at 5:8 Comment(3)
Totally slick! Thank you!Splurge
broken link, can you please explain @Joe WhiteUltimogeniture
@Hacki I updated the broken link, thanks for the heads-up.Silassilastic
M
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

Mcvay answered 24/1, 2011 at 17:35 Comment(1)
Use BeginInvoke instead of that scary Sleep(100) statement.Blackmail
G
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
        }
Gerladina answered 15/2, 2013 at 14:44 Comment(1)
Only works if the control is directly inside the Window, not if it's nested inside a UserControl.Silassilastic
A
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.

Atwater answered 27/2, 2015 at 22:50 Comment(4)
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.Clapp
That doesn't surprise me @heringer... that would be like trying to set focus on a <border> 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.Atwater
I have used this approach on a stackpanel to set which child button I wanted to focus once the form is loaded. Thanks a lotRickety
Be careful, it may make bindings totally broken. #30677363Erikerika
C
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" />
Crustacean answered 2/2, 2017 at 17:16 Comment(0)
T
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.

Transept answered 11/2, 2018 at 21:1 Comment(0)
F
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

Fireresistant answered 2/2, 2018 at 8:3 Comment(0)
C
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>
Conspire answered 26/4, 2015 at 14:11 Comment(0)
A
-1
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">
Araceli answered 23/6, 2012 at 19:8 Comment(2)
Please provide more context to your answer.Sacking
Exact duplicate of this answer posted almost three years ago.Silassilastic

© 2022 - 2024 — McMap. All rights reserved.