Q044. WPF で Tab オーダーを細かく設定する方法は?

A. Windows Forms なら、デザイナでフォームを開き、「表示」→「タブオーダー」で現在のタブインデックスが表示され、クリック順でタブインデックスを設定することができます。



関連記事Windows アプリケーションの利点
関連記事コントロールのTabIndex順で処理したいことってあるよね。階層のTabIndexを表現してみよう。


残念なことに WPF にはそのような便利な機能はありません。WPF の場合、タブインデックスは基本的に XAML の要素順になります。
任意にタブオーダーを指定したいなら TabIndex プロパティを明示的に設定する方法もありますが、UserControl にTabIndex を設定すると、パレント側の TabIndex と衝突し想定外のコントロールにフォーカスがジャンプしたり、同じ UserContorol を複数貼り付けると、UserControl内の同じコントロールでタブ遷移を行ってしまう等、想定外の事態が発生します。


例えば以下のような UserContorol があるとします。各コントロールには 明示的に TabIndex が設定されています。

<UserControl x:Class="UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="200" d:DesignWidth="150"
             >
    <StackPanel >
        <TextBox Name="TextBox1" Height="20" TabIndex="1" />
        <TextBox Name="TextBox2" Height="20" TabIndex="2" />
        <TextBox Name="TextBox3" Height="20" TabIndex="3" />
        <Button Name="Button1" Height="20" Content="Button1" TabIndex="4" />
        <Button Name="Button2" Height="20" Content="Button2" TabIndex="5" />
    </StackPanel>
</UserControl>


この UserControl を Window に二つ配置します。

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="250" Width="300" xmlns:my="clr-namespace:WpfApplication1">
    <Grid>
        <my:UserControl1 HorizontalAlignment="Left" Margin="30,40,0,0" 
                         x:Name="UserControl11" VerticalAlignment="Top" />
        <my:UserControl1 HorizontalAlignment="Left" Margin="90,40,0,0" 
                         x:Name="UserControl12" VerticalAlignment="Top" />
    </Grid>
</Window>


タブ遷移は UserControl 内を移動せず、以下のように Window に配置した二つのUserContorl 間で交互に移動します。これでは話になりません。('A`)

UserControl11.TextBox1UserControl12.TextBox1UserControl11.TextBox2UserControl12.TextBox2 ・・・



そこで XAML で TabIndex を細かく制御するには KeyboardNavigation.TabNavigation を使うといいみたいです。
コントロールのコンテナである StackPanel に添付プロパティ KeyboardNavigation.TabNavigation を設定します。KeyboardNavigation.TabNavigationLocal を指定すると、タブインデックスはコンテナ内で優先され、要素の終端に達するとコンテナからフォーカスが離れます。

<UserControl x:Class="UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="200" d:DesignWidth="150"
             >
    <StackPanel KeyboardNavigation.TabNavigation ="Local" >
        <TextBox Name="TextBox1" Height="20" TabIndex="1" />
        <TextBox Name="TextBox2" Height="20" TabIndex="2" />
        <TextBox Name="TextBox3" Height="20" TabIndex="3" />
        <Button Name="Button1" Height="20" Content="Button1" TabIndex="4" />
        <Button Name="Button2" Height="20" Content="Button2" TabIndex="5" />
    </StackPanel>
</UserControl>


これでタブフォーカスは以下のように UserControl 内を移動するようになりました。

UserControl11.TextBox1UserControl11.TextBox2UserControl11.TextBox3UserControl11.Button1 ・・・


関連記事KeyboardNavigationMode 列挙体


WPF FAQ の目次に戻る