Search code examples
wpfxamlfocustab-ordering

Specified Tab Order is not being followed?


Why isn't my XAML following the TabOrder I specified?

I currently have:

<DockPanel>
    <Grid DockPanel.Dock="Top">
        <UserControl TabIndex="0">
            <StackPanel Orientation="Horizontal">
                <ComboBox />
                <TextBox Text="Search Text" />
                <Button Content="Search" />
            </StackPanel>
        </UserControl>
        <ComboBox TabIndex="1" />
        <Separator />
        <TextBox TabIndex="3" Text="Save" />
        <TextBox TabIndex="4" Text="Cancel" />
    </Grid>
    <Grid>
        <ContentControl TabIndex="2" />
        <Popup />
    </Grid>
</DockPanel>

enter image description here

My TabOrder should go

  • Search ComboBox
  • Search Text
  • Search Button
  • Database ComboBox
  • ContentControl
  • Save Button
  • Cancel Button

But instead it goes

  • Search ComboBox
  • Search Text
  • Search Button
  • ContentControl
  • Database ComboBox
  • Save Button
  • Cancel Button

What do I have wrong with my TabOrder?

Edit

I found this SO answer which suggested making UserControl.IsTabStop="False", and binding it's Child control's TabIndex to UserControl.TabIndex, which partially works.

My TabOrder is now

  • Search ComboBox
  • Search Text
  • Search Button
  • Database ComboBox
  • Save Button
  • Cancel Button
  • ContentControl

Solution

  • Apparently by default, WPF reads all the controls, inside and outside UserControls, at the same tab level (unless specified otherwise). Since the controls inside the UserControl do not have a TabIndex specified, they get tabbed to last after the first tab cycle.

    The workaround was to bind the TabIndex of the inner controls to the TabIndex of the UserControl

    <DockPanel Margin="10" KeyboardNavigation.TabNavigation="Cycle">
        <Grid DockPanel.Dock="Top"
              local:GridProperties.ColumnCount="6"
              local:GridProperties.StarColumns="0">
    
            <TextBlock Text="Header" FontSize="20" FontWeight="Bold" />
            <ContentControl Grid.Column="1" TabIndex="0" IsTabStop="False" Content="{Binding SearchViewModel}" />
    
            <ComboBox Grid.Column="2" Margin="5" Width="100" />
    
            <Separator Grid.Column="3" Style="{StaticResource VerticalSeparatorStyle}" />
    
            <Button Grid.Column="4" TabIndex="3" Content="Save" Width="75" Margin="5" />
            <Button Grid.Column="5" TabIndex="4" Content="Cancel" Width="75" Margin="5" />
        </Grid>
    
        <Line HorizontalAlignment="Stretch" X2="1" Stretch="Fill" Stroke="Black" StrokeThickness="1" Margin="0,5" DockPanel.Dock="Top" />
    
        <Grid x:Name="ShellContentRoot">
    
            <!-- Current Page -->
            <ContentControl TabIndex="2" Content="{Binding CurrentAccount}" IsTabStop="False" />
    
            <!-- Search Results -->
            <local:PopupPanel local:PopupPanel.PopupParent="{Binding ElementName=ShellContentRoot}" />
        </Grid>
    </DockPanel>
    

    The only thing special about my SearchView is that the controls all set

    TabIndex="{Binding Path=TabIndex, RelativeSource={RelativeSource 
        AncestorType={x:Type local:SearchView}}}"
    

    Tab Order goes:

    • UserControl Search ComboBox
    • UserControl Search Text
    • UserControl Search Button
    • Database ComboBox
    • ContentControl
    • Save Button
    • Cancel Button