コントロールのフォーカスのスタイルと FocusVisualStyle

Windows Presentation Foundation (WPF) では、キーボード フォーカスを受け取ったときにコントロールの外観を変更するためのメカニズムが、並列的に 2 つ用意されています。 1 つ目のメカニズムは、コントロールに適用されるスタイルやテンプレート内で、IsKeyboardFocused などのプロパティに対するプロパティ セッターを使用する方法です。 2 つ目のメカニズムは、FocusVisualStyle プロパティの値として個別のスタイルを指定する方法です。"フォーカス表示スタイル" を使用すると、コントロールやその他の UI 要素のビジュアル ツリーを置き換えて変更するのではなく、コントロール上に描画する装飾用の独立したビジュアル ツリーを作成することができます。 このトピックでは、これらの各メカニズムがどのような場合に適しているかについて説明します。

フォーカス表示スタイルの目的

フォーカス表示スタイルは、UI 要素へのキーボード ナビゲーションに基づいてビジュアルなユーザー フィードバックを表示するための、共通の "オブジェクト モデル" を提供する機能です。 これは、コントロールに新しいテンプレートを適用したり、特定のテンプレート構成を把握したりしなくても利用できます。

ただし、フォーカス表示スタイル機能はコントロール テンプレートを把握せずに動作するので、フォーカス表示スタイルを使用したコントロールに表示できるビジュアル フィードバックは、必然的に限定されます。 この機能によって実際に行われることは、テンプレートを使用したコントロールのレンダリングによって作成されたビジュアル ツリーの上に、別のビジュアル ツリー (装飾) を重ね合わせるという処理です。 この個別のビジュアル ツリーは、FocusVisualStyle プロパティに従ったスタイルを使用して定義します。

既定のフォーカス表示スタイル動作

フォーカス表示スタイルは、フォーカス アクションがキーボードによって開始された場合にのみ機能します。 マウス操作やプログラムによるフォーカス変更の場合、フォーカス表示スタイルのモードは無効になります。 各フォーカス モードの違いについて詳しくは、「フォーカスの概要」を参照してください。

コントロールのテーマには、既定のフォーカス表示スタイル動作があり、その動作が、そのテーマ内のすべてのコントロールのフォーカス表示スタイルになります。 このテーマ スタイルは、静的キー FocusVisualStyleKey の値によって識別されます。 アプリケーション レベルで独自のフォーカス表示スタイルを宣言すると、この既定のスタイル動作が置き換えられます。 テーマ全体を定義する場合は、その同じキーを使用して、テーマ全体の既定動作のスタイルを定義する必要があります。

テーマ内では、通常、既定のフォーカス表示スタイルは非常にシンプルなものです。 大まかに表すと、次のようになります。

<Style x:Key="{x:Static SystemParameters.FocusVisualStyleKey}">
  <Setter Property="Control.Template">
    <Setter.Value>
      <ControlTemplate>
        <Rectangle StrokeThickness="1"
          Stroke="Black"
          StrokeDashArray="1 2"
          SnapsToDevicePixels="true"/>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

フォーカス表示スタイルをいつ使用するか

基本的な考え方として、コントロールに適用されるフォーカス表示スタイルの外観は、各コントロール間で一貫している必要があります。 一貫性を維持するための 1 つの方法として、フォーカス表示スタイルを変更するのは、テーマ全体を構成する場合だけに限定するという方法があります。その場合、テーマ内で定義されている各コントロールに、まったく同じフォーカス表示スタイルを使用することもできますし、見た目の共通性を維持しながら、コントロールごとにスタイルのバリエーションを持たせることもできます。 また、ページや UI の要素のうち、キーボードでのフォーカス取得が可能なすべて要素について、同じスタイル (または類似のスタイル) を使ってスタイルを指定することもできます。

テーマに属していない個々のコントロール スタイルに対して FocusVisualStyle を設定することは、フォーカス表示スタイルの本来の用途ではありません。 表示動作がコントロール間で一貫していないと、キーボードのフォーカスに関してユーザーが混乱する可能性があるからです。 キーボード フォーカスに関する動作として、テーマ全体での一貫性から外れたものを特定のコントロールに意図的に指定したい場合は、個別の入力状態プロパティ (IsFocusedIsKeyboardFocused など) に対応したスタイルのトリガーを使用する方がアプローチとして適しています。

フォーカス表示スタイルは、キーボード フォーカス専用です。 そのため、フォーカス表示スタイルはユーザー補助機能の一種と言えます。 あらゆるタイプのフォーカス (マウス、キーボード、またはプログラム) を対象に UI を変更したい場合は、フォーカス表示スタイルを使用するのではなく、一般的なフォーカス プロパティ (IsFocusedIsKeyboardFocusWithin など) の値によって動作するスタイルやテンプレートで、セッターやトリガーを使用するようにしてください。

フォーカス表示スタイルの作成方法

フォーカス表示スタイル用に作成するスタイルでは、常に TargetTypeControl にします。 このスタイルは、主に ControlTemplate で構成される必要があります。 ターゲットの種類として、そのフォーカス表示スタイルが FocusVisualStyle に割り当てられている種類を指定することはしないでください。

ターゲットの種類は常に Control となるので、すべてのコントロールに共通するプロパティを使用してスタイルを設定する必要があります (Control クラスとその基本クラスのプロパティを使用します)。 テンプレートを作成する際には、UI 要素へのオーバーレイとして正しく機能すると共に、コントロールの機能領域が不明瞭にならないようにする必要があります。 つまり、ビジュアル フィードバックがコントロールの余白の外側に表示されるようにするか、一時的な (または目立ち過ぎない) 効果として表示されるようにして、フォーカス表示スタイルが適用されたコントロールに対するヒット テストの邪魔にならないようにする必要があります。 テンプレートのバインドで使用できるプロパティのうち、オーバーレイ テンプレートのサイズや位置を決定するのに役立つものとしては、ActualHeightActualWidthMarginPadding があります。

フォーカス表示スタイルの代替手段

1 つのコントロールだけにスタイルを設定する場合や、コントロール テンプレートをより細かく制御したい場合など、フォーカス表示スタイルを使用するのが適切でない場合でも、フォーカスの変化に応じて表示動作を作成できるプロパティは他にもたくさんあります。

トリガー、セッター、およびイベント セッターについては、「スタイルとテンプレート」で詳しく説明しています。 ルーティング イベントの処理については、「ルーティング イベントの概要」で説明しています。

IsKeyboardFocused

キーボード フォーカスに特化して対応したい場合は、IsKeyboardFocused 依存関係プロパティをプロパティ Trigger に使用することができます。 1 つのコントロールだけを対象にする場合で、他のコントロールのキーボード フォーカス動作とは見た目が調和しない可能性がある場合は、スタイルまたはテンプレートでプロパティ トリガーを使用すると、キーボード フォーカス動作をより効果的に定義することができます。

これに似たもう 1 つの依存関係プロパティとして、IsKeyboardFocusWithin も使用できます。これは、キーボード フォーカスが複合的なコントロール領域や機能領域内にあることを視覚的に示したい場合に適しています。 たとえば、複数のコントロールをグループ化したパネルがある場合、IsKeyboardFocusWithin トリガーを配置すれば、キーボード フォーカスがそのパネル内の個別の要素にまで絞られていない場合でも、そのパネルの表示が変わるようにすることができます。

また、GotKeyboardFocus イベントと LostKeyboardFocus イベント (およびプレビュー版での同等機能) を使用することもできます。 これらのイベントは、EventSetter の基礎として使用できます。また、分離コード内のイベントのハンドラーを記述することもできます。

その他のフォーカス プロパティ

フォーカス変更のあらゆる原因に対応して表示動作を生成したい場合は、セッターやトリガーのベースとして IsFocused 依存関係プロパティを使用するか、GotFocus または LostFocus イベントを EventSetter に使用するようにしましょう。

関連項目