WPFで複数のラジオボタングループを使用している場合、Tabキーを使ったフォーカス移動をカスタマイズしたいという要件に対する解決方法をご紹介します。特に、グループ内の選択済みのラジオボタンにのみフォーカスが移動するようにします。
問題の発生
後輩から、複数グループのラジオボタンの場合に、Tabキーでフォーカス移動を行う際、グループ内の全てのラジオボタンを一つずつ突翻するのではなく、グループごとに選択されたラジオボタンにしか移動しないようにしたいとの相談がありました。下図のようです。
KeyDownイベントでTabキーのフォーカス制御もちろんできますが、ちょっと面倒になります(実際の画面は別のコントロールもたくさんあります)。今回はコードビハインドなし、xmalだけでこの動作実現したいと思っています。
解決策1: TabIndexの制御
まず考えたのは、TabIndex
の制御です。各グループ内で選択されたラジオボタンにのみTabIndex
をフォーカス順に設定し、選択されていないラジオボタンにはTabIndex
を-1
に設定します。以下のように、各グループにラジオボタンのスタイルを追加します。
基本のスタイル設定
デフォルトでTabIndex
を-1
に設定し、IsChecked=True
のラジオボタンにはフォーカス順に従った値を設定します。
<StackPanel.Resources>
<Style TargetType="RadioButton">
<Setter Property="TabIndex" Value="-1"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TabIndex" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
これにより、最初の段階ではグループごとに選択されたラジオボタンにのみフォーカスが移動するようになります。ただし、次のような問題が発生します。
問題点
- 最後のグループから戻る際に、選択されていないラジオボタンにフォーカスが移動してしまう。
- フォーカスが選択されていないラジオボタン間を移動する可能性がある。
原因は、Tabキーのフォーカス移動がTabIndex
の昇順で行われるためです。一番大きなTabIndex
の要素の次に、一番小さなTabIndex
の要素(-1
のラジオボタン)に移動してしまいます。
解決策2: フォーカスされたラジオボタンのTabIndexを動的に設定
次に、フォーカスされたラジオボタンのTabIndex
も移動順に設定する方法を追加します。以下のトリガーをスタイルに追加します。
<Trigger Property="IsFocused" Value="True">
<Setter Property="TabIndex" Value="1"/>
</Trigger>
これにより、選択されていないラジオボタン間でフォーカスが移動することを防ぐことができます。ただし、まだ次の問題が残ります。
問題点
最後のグループから戻る際に、選択されていないラジオボタン(TabIndex=-1
)にフォーカスが移動してしまいます。
解決策3: 最後のグループでのTabIndexを動的に調整
最後のグループで戻る際に選択されていないラジオボタンにフォーカスが移動しないように、フォーカスされたラジオボタンのTabIndex
を0
に設定します。
修正後のスタイル
<Style TargetType="RadioButton">
<Setter Property="TabIndex" Value="-1"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="TabIndex" Value="4"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="TabIndex" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
この設定により、最後のグループから戻る際にも、選択されていないラジオボタンにフォーカスが移動することを防ぐことができます。
結果
上記の設定を組み合わせることで、Tabキーによるフォーカス移動が期待通りに動作します。グループ内の選択されたラジオボタンにのみフォーカスが移動し、選択されていないラジオボタン間での不要な移動が防止されます。
コメント