How to prevent tab navigation to a control, while allowing directional navigation to that control

7.9k views Asked by At

In WPF (MVVM, no code-behind), say I have the following xaml:

<StackPanel>
    <Button>Normal 1</Button>
    <Button>Special</Button> <!--This button should not tab focus, but still focus via arrow keys-->
    <Button>Normal 2</Button>
</StackPanel>

By default, you can (1) tab between the buttons or (2) up/down arrow between the buttons. I want to make one of the buttons -- the Special button -- not focusable via tab, yet still focusable via up/down. I tried IsTabStop="False", but that also prevents focusing that button via directional navigation (up/down arrows).

How can I remove a single button from tab navigation, while continuing to allow directional navigation? Tabbing and arrowing should be unaffected for all other controls.

I've tried combinations of IsTabStop, KeyboardNavigation.TabNavigation, and KeyboardNavigation.DirectionalNavigation to no avail. Maybe I haven't found the right mix. Or maybe there is another approach. Ideas?


EDIT: Okay, I am adding a bounty. To be clear, I am looking for a MVVM, no code-behind way to decorate a control (e.g. via style, attached property, attached behavior, etc), such that it is removed from the tab order, while remaining a valid directional navigation target. Hard coding knowledge of the controls into the Window (or similar), is not acceptable. This needs to be a general, reusable solution. Something like: <Button AllowDirectionalNavigationButPreventTabNavigation="True"/>.

2

There are 2 answers

3
Nick Sologoub On BEST ANSWER

How about that option. You stick your special button into another container and put KeyboardNavigation.TabNavigation="None" on that container. I've just tried it and it looks like I can achieve what you need.

<StackPanel >
  <Button>Normal 1</Button>
  <StackPanel KeyboardNavigation.TabNavigation="None">
    <Button>Special</Button><!--This button should not tab focus, but still focus via arrow keys-->
  </StackPanel>
  <Button>Normal 2</Button>
</StackPanel>
3
SamTh3D3v On

here how i did it :

<Window x:Class="MvvmLight1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ignore="http://www.ignore.com"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:command="http://www.galasoft.ch/mvvmlight"
    mc:Ignorable="d ignore"
    Height="300"
    Width="300"
    DataContext="{Binding Main, Source={StaticResource Locator}}">
<i:Interaction.Triggers>
    <i:EventTrigger EventName="KeyDown">
        <command:EventToCommand Command="{Binding Mode=OneWay,Path=KeyDownCommand}" PassEventArgsToCommand="True"/>
    </i:EventTrigger>
</i:Interaction.Triggers>
<Grid x:Name="LayoutRoot">
    <StackPanel>
        <Button>Normal 1</Button>
        <Button>Special</Button>
        <!--Should not tab focus, yet still focus via arrow keys-->
        <Button>Normal 2</Button>
    </StackPanel>
</Grid>

i am using the mvvmlight, and the EventToCommand is used to intercept the KeyDown Event and direct it to the following command in the ViewModel:

 private RelayCommand<KeyEventArgs> _keyDownCommand;
    public RelayCommand<KeyEventArgs> KeyDownCommand
    {
        get
        {
            return _keyDownCommand
                ?? (_keyDownCommand = new RelayCommand<KeyEventArgs>(
                (s) =>
                {
                    if (s.Key == Key.Tab)
                    {
                        s.Handled = true;
                    }
                }));
        }
    }