4 Comments

Using XAML to Set Focus on a Textbox in WPF Desktop Applications

Recently, I have been using WPF to give a .NET desktop application its own customized look and feel that matches my client’s product branding.

In one of the workflows I implemented last week, the user inputs a 4-digit passcode that allows them to connect to an external device.

Here is a diagram of the workflow we needed to implement:

passcode_entry_workflow

Instead of one TextBox control for the entire code, we wanted to have 4 seperate TextBox controls. This will indicate to the user that they need to have a code with 4 digits. However, it would be very annoying if we made the user tab between each TextBox as they entered their passcode. Therefore, as the user types in each digit, we will need to automatically move focus to the next TextBox for them.

The Controls

Here are the controls I used to achieve this interaction:

<Grid Style="{StaticResource LayoutRoot}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="Enter your passcode to continue." />
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox Style="{StaticResource CodeDigit}" Name="CodeDigit1" />
            <TextBox Style="{StaticResource CodeDigit}" Name="CodeDigit2"/>
            <TextBox Style="{StaticResource CodeDigit}" Name="CodeDigit3"/>
            <TextBox Style="{StaticResource CodeDigit}" Name="CodeDigit4" Margin="0,12,0,12" />
        </StackPanel>
        <Button Name="ConnectButton" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="20,10,20,10" Content="Connect" />
    </Grid>

I have a grid containing a TextBlock for the explanation text. The four TextBlocks are contained in a StackPanel, and each TextBlock has a unique Name. Last comes the Button for the connection interaction. Each TextBox has a Name, as does the Button. These will become important when we set up the interaction.

Next, in a separate ResourceDictionary, I created styles for the Grid and the TextBoxes. We’ll take a closer look at those in a moment.

How to Set Focus on a TextBox Using Triggers in XAML

This seems like a relatively easy thing to do. The TextBox Class has an IsFocused property. So it would be natural to assume that you could just use a Setter inside a Trigger on the TextBox to set IsFocused to True. However, this doesn’t work. IsFocused is a read-only property inherited from UIElement, so it cannot be set with a Setter. What are we going to do?

Instead of using a Setter on the TextBox, we’ll have to use the FocusManager. The FocusManger controls focus within a specific focus scope. So by setting FocusManager.FocusedElement on our FocusScope, we can acheive focus on a textbox.

<Style x:Key="LayoutRoot" TargetType="Grid">
        <Style.Triggers>
            <!--these are the triggers that make the code cursor jump from box 1 to box 2 to box 3.-->
            <DataTrigger Binding="{Binding ElementName=CodeDigit1, Path=Text.Length}" Value="1">
                <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=CodeDigit2}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=CodeDigit2, Path=Text.Length}" Value="1">
                <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=CodeDigit3}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=CodeDigit3, Path=Text.Length}" Value="1">
                <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=CodeDigit4}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=CodeDigit4, Path=Text.Length}" Value="1">
                <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=ConnectButton}"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>

Here, I used DataTriggers on the Grid that contains all of my controls to detect when a digit was entered into each TextBox. When a digit is entered into the TextBox named CodeDigit1, the Trigger moves the focus to the TextBox named CodeDigit2. It will continue on down to CodeDigit3 and CodeDigit4, and finally, after all 4 digits are entered, it will give focus to the ConnectButton at the end. Nifty.

Because of the nature of the workflow in my app, I could use a Trigger on the Text.Length like this. Depending on your specific implementation, you may need to choose a different property for your trigger.

Working Demo

I have created a small working demo of this interaction. Click here to download the TextBox Focus Demo.