27
votes

I have a listbox that in which every item is represented using a textbox. The thing is that I want to be able to tab between all the items in the listbox before moving to the next element in the xaml window.

The current (and normal WPF behavior) is that when I tab into the listbox, the first element is highlighted, if I tab again, then the focus moves into the textbox inside that item. If I tab once again, focus moves to the next element in the window (without going through any of the other items in the ListBox).

The behavior I want is the following: When I tab into the listbox, the first textbox obtains focus automatically (without highlighting the whole Item)*. If I tab again then the next textbox in the listbox gets focus. When I tab at the last textbox in the listbox, then focus moves to the next control.

*I already how to do this, I'm only posting it here in order to explain the complete process.

I've been looking for the solution and I've been unable to find something.

2

2 Answers

72
votes

This can be done in xaml, by setting the following two properties.

    <Style TargetType="ListBox" >
        <Setter Property="KeyboardNavigation.TabNavigation" Value="Continue" />
    </Style>

    <Style TargetType="ListBoxItem">
        <Setter Property="IsTabStop" Value="False" />
    </Style>

For full explanation see Derek Wilson's Blog post.

0
votes

EDIT

After the comment, to be specific:

private void ListBox_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Tab)
    {
        ListBox lb = sender as ListBox;

        if(lb == null) return;

        if(lb.SelectedIndex < lb.Items.Count - 1)
        {
            GiveItemFocus(lb, lb.SelectedIndex + 1, typeof(TextBox));
            e.Handled = true;
        }
    }
}

private void GiveItemFocus(ListBox lb, int index, Type descentdantType)
{
    if(lb.Items.Count >= index || index < 0)
    {
        throw new ArgumentException();
    }

    ListBoxItem lbi = (ListBoxItem) lb.ItemContainerGenerator.ContainerFromIndex(index);

    lb.UnselectAll();

    lbi.IsSelected = true;

    UIElement descendant = (UIElement) FindVisualDescendant(lbi, o => o.GetType() == descentdantType);

    descendant.Focus();
}

private static DependencyObject FindVisualDescendant(DependencyObject dependencyObject, Predicate<bool> condition)
{
    //implementation not provided, commonly used utility
}

Setting e.Handled to true will ensure only your handler is processed on a tab press, and the default behaviour will not be activated.