WPF - How do I set the Tab Index to go straight to the textbox inside a ContentControl?

40 Views Asked by At

I have a custom TextBox template that I've defined inside a DataTemplate. I use a ContentControl to create these custom TextBoxes in my views.

The TextBoxes consist of a TextBox with space underneath it to display an error message if needed (binded to a custom class I've defined as 'TextValidation').

When I have a series of these custom TextBoxes, I expect to press tab in one text box and end up in the next text box. However, whenever I press Tab, it highlights the entire ContentControl. Then on the SECOND press of tab, the cursor would enter into the textbox.

I want to be able to press tab and go from the textbox in one ContentControl straight into the textbox in the next.

Here is my view where I create two textboxes:

<ContentControl Content="{Binding Text1}" Margin="0 0 0 10"
                ContentTemplate="{StaticResource TextBoxTemplate}"/>
<ContentControl Content="{Binding Text2}" Margin="0 0 0 10"
                ContentTemplate="{StaticResource TextBoxTemplate}"/>

Text1 and Text2 are from a custom class I've defined as 'TextValidation'

public class TextValidation
    {
        public string Text;
        public bool Error;
        public string ErrorMsg;
    }

TextBoxTemplate is defined in a ResourceDictionary (I've removed all the irrelevante setters/styling):

<DataTemplate x:Key="TextBoxTemplate">
        <TextBox>
            <TextBox.Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="Text" Value="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>
                <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type TextBox}">
                                <StackPanel>
                                    <Border x:Name="border" CornerRadius="3"
                                        Background="White"
                                        BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                                        SnapsToDevicePixels="True">
                                        <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                                    </Border>
                                    <StackPanel Orientation="Horizontal" x:Name="errorMsg" Height="20">
                                        <TextBlock FontSize="12" FontStyle="Italic" Foreground="#ff3333"
                                    Text="{Binding ErrorMsg}" />
                                    </StackPanel>
                                </StackPanel>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TextBox.Style>
        </TextBox>
</DataTemplate>

Anyone know how I can fix the tab indexes so I can go straight from one textbox to another?

2

There are 2 best solutions below

0
teriyakiforandy On

The focus shifts to the ContentControl. I simply added "IsTabStop="false" to the ContentControl and that fixed it.

2
Rob van Daal On

I would also google for KeyboardNavigationMode and TabNavigation because there's more to it.

But i would start with removing the ContentControls from the visual tree, because this is a cumbersome way to set a DataContext to the TextBox. The Content of a ContentControl serves as the DataContext for the elements in the ContentTemplate. So this should also work:

<Grid>
    <Grid.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Text" Value="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TextBox}">
                        <StackPanel>
                            <Border x:Name="border" CornerRadius="3"
                                    Background="White"
                                    BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                                    SnapsToDevicePixels="True">
                                <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                            </Border>
                            <StackPanel Orientation="Horizontal" x:Name="errorMsg" Height="20">
                                <TextBlock FontSize="12" FontStyle="Italic" Foreground="#ff3333"
                                Text="{Binding ErrorMsg}" />
                            </StackPanel>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Grid.Resources>
    <TextBox DataContext="{Binding Text1}" Margin="0 0 0 10"/>
    <TextBox DataContext="{Binding Text2}" Margin="0 0 0 10"/>
</Grid>

And don't forget to implement INotifyPropertyChanged in your TextValidation class. When this is not necessary because the TextBox only updates the source and not the other way around you could/should put Mode=OneWayToSource in your Style setter Text binding. But at least the ErrorMsg binding needs the implementation of INotifyPropertyChanged.