Keyboard Friendly TextBox

Building keyboard-friendly interfaces can be tricky. For screens with multiple fixed-length TextBoxes, users don’t want to constantly press the tab key to move from one TextBox to the next. In addition, when tabbing between TextBoxes it’s reasonable to assume that if the user start to type, they want the content to be replaced. Manually selecting the text after each tab is cumbersome.

This article shows how to build a custom WPF TextBox that handles both situations. I’ll create a product key input screen as it’s a good example of using multiple fixed-length TextBoxes:

Here’s the XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <TextBlock Grid.Row="0" Text="Enter product key:" Margin="10" />
    <StackPanel Grid.Row="1" Orientation="Horizontal">
        <co:CustomTextBox x:Name="TextBox1" MaxLength="4" NextControl="{Binding ElementName=TextBox2}" Height="23" Width="53" Margin="10" />
        <co:CustomTextBox x:Name="TextBox2" MaxLength="4" NextControl="{Binding ElementName=TextBox3}" Height="23" Width="53" Margin="10" />
        <co:CustomTextBox x:Name="TextBox3" MaxLength="4" NextControl="{Binding ElementName=TextBox4}" Height="23" Width="53" Margin="10" />
        <co:CustomTextBox x:Name="TextBox4" MaxLength="4" NextControl="{Binding ElementName=OkButton}" Height="23" Width="53" Margin="10" />
        <Button x:Name="OkButton" Content="OK" Height="23" Width="53" Margin="10" />
    </StackPanel>
</Grid>

Each TextBox has a MaxLength of 4. When the length of TextBox’s Text property reaches the MaxLength, we want to automatically switch the focus to the next Control:

If the user tabs between completed fields, the content should be automatically selected:

In order for the TextBox to automatically change the focus to the next control, we need to associate that control with the TextBox. Note this only makes sense when we have the MaxLength set. Here’s the code for the control:

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

namespace CodeOverload.Wpf.Controls
{
    public class CustomTextBox : TextBox
    {
        public static readonly DependencyProperty NextControlProperty =
            DependencyProperty.Register("NextControl", typeof(UIElement), typeof(CustomTextBox));

        /// <summary>
        /// Gets or sets the next control to focus when this TextBox is full.
        /// </summary>
        public UIElement NextControl
        {
            get { return (UIElement)GetValue(NextControlProperty); }
            set { SetValue(NextControlProperty, value); }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomTextBox"/> class.
        /// </summary>
        public CustomTextBox() : base()
        {
            // Selects all the text when the textbox is in focus
            this.GotFocus += delegate(object sender, System.Windows.RoutedEventArgs e)
            {
                this.SelectAll();
            };

            // Moves the control to the next focus target when the box is full
            this.TextChanged += delegate(object sender, TextChangedEventArgs e)
            {
                ChangeFocus(sender, NextControl);
            };
        }

        /// <summary>
        /// Changes the focus.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="target">The target.</param>
        private void ChangeFocus(object source, UIElement target)
        {
            TextBox textBox = source as TextBox;
            if (textBox != null)
            {
                int trigger = textBox.MaxLength;
                if (trigger > 0 && textBox.Text.Length >= trigger)
                    target.Focus();
            }
        }
    }
}

Note the use of UIElement as the target type. We want to be able to target any control for focus, not just TextBoxes.

Lastly, remember to bring the control’s namespace into scope at the top of XAML file:

xmlns:co="clr-namespace:CodeOverload.Wpf.Controls"
Advertisements
This entry was posted in Tips and Tricks and tagged , , . Bookmark the permalink.

One Response to Keyboard Friendly TextBox

  1. Sean says:

    I would change the TextChanged event subscription to the following to avoid NullReferenceException from being thrown when NextControl is not specified…

    if (NextControl != null)
    {
    // Moves the control to the next focus target when the box is full
    this.TextChanged += delegate(object sender, TextChangedEventArgs e)
    {
    ChangeFocus(sender, NextControl);
    };
    }

    Great little control otherwise, thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s