4

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"/>.

Matt Jacobi
  • 864
  • 7
  • 17
  • That should help : [WPF: How to disable tab navigation without also disabling arrow key navigation?][1] [1]: http://stackoverflow.com/questions/4210659/wpf-how-to-disable-tab-navigation-without-also-disabling-arrow-key-navigation – SamTh3D3v Dec 05 '14 at 22:40
  • @Joseph I found that question, but that person had a different scenario (they were using a listview), and neither of the suggested solutions solve my exact issue. Plus, the accepted solution uses code-behind and requires the window to know about the tab behavior of all of controls within it -- I want the control to dictate it's own behavior. – Matt Jacobi Dec 05 '14 at 22:44

2 Answers2

12

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>
Nick Sologoub
  • 724
  • 1
  • 7
  • 21
  • 1
    This is the correct solution; the `TabNavigation` property controls the logical navigation behavior of an element's *children*, so in the OP's case, the "special" button should be wrapped in another container. – Mike Strobel Dec 09 '14 at 14:35
  • 1
    Perfect! My real scenario was more complicated, but this was the idea/info I needed to get it there. In my custom control's style, I set `Focusable=False` and `KeyboardNavigation.TabNavigation=None`. Then, in the custom control's control template, I set `Focusable=True` and it achieves exactly what I wanted. – Matt Jacobi Dec 09 '14 at 16:53
  • If you do this on a button contained into a Grid, it works at first glance. But if you click on the button, the focus still come back on the button (I'm using MaterialDesignToolkit). Any idea to conclude this issue ? –  Apr 14 '20 at 14:26
0

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;
                    }
                }));
        }
    }
SamTh3D3v
  • 9,854
  • 3
  • 31
  • 47
  • 1
    Thanks, but this disables tab for the entire window. I'm looking for a way to disable tab for only one control. – Matt Jacobi Dec 06 '14 at 02:01
  • What control! do you mean the StackPanel ? – SamTh3D3v Dec 06 '14 at 09:21
  • 1
    For just the one button. The special button. All other buttons should still be tabbable. The question is: how to remove a control (e.g. button) from the tab order, while leaving it as a valid directional navigation target. – Matt Jacobi Dec 06 '14 at 20:59