フォーカスの概要

WPF には、キーボード フォーカスと論理フォーカスという、フォーカスに関する 2 つの主要な概念があります。 キーボード フォーカスはキーボード入力を受け取る要素を指し、論理フォーカスはフォーカスを持つフォーカス範囲内の要素を指します。 これらの概念については、この概要で詳しく説明します。 フォーカスを取得可能な領域を複数持つ複雑なアプリケーションを作成する場合は、これらの概念の違いを理解することが重要です。

フォーカス管理に関与する主要なクラスには、Keyboard クラス、FocusManager クラス、および UIElementContentElement などの基本要素クラスがあります。 基本要素の詳細については、「基本要素の概要」を参照してください。

Keyboard クラスは主にキーボード フォーカスに関連し、FocusManager は主に論理フォーカスに関連しますが、これは絶対的な区別ではありません。 キーボード フォーカスを持つ要素は論理フォーカスも持ちますが、論理フォーカスを持つ要素は必ずしもキーボード フォーカスを持ちません。 Keyboard クラスを使用してキーボード フォーカスを持つ要素を設定したときには、要素に論理フォーカスも設定されるので、この違いがよくわかります。

キーボード フォーカス

キーボード フォーカスは、現在キーボード入力を受け取っている要素を指します。 キーボード フォーカスを持つ要素は、デスクトップ全体で 1 つしかありません。 WPF では、キーボード フォーカスを持つ要素の IsKeyboardFocused は、true に設定されます。 Keyboard クラスの静的プロパティ FocusedElement では、現在キーボード フォーカスを持っている要素が取得されます。

要素でキーボード フォーカスを取得するには、基本要素で Focusable プロパティと IsVisible プロパティを true に設定する必要があります。 Panel 基底クラスなどの一部のクラスでは、Focusable が既定で false に設定されます。したがって、そのような要素でキーボード フォーカスを取得できるようにする場合は、Focusabletrue に設定する必要があります。

キーボード フォーカスは、要素への Tab キーでの移動や特定の要素でのマウスのクリックなど、UI を使用したユーザー操作を通じて取得できます。 キーボード フォーカスは、プログラムで Keyboard クラスの Focus メソッドを使用して取得することもできます。 Focus メソッドでは、指定した要素へのキーボード フォーカスの設定が試みられます。 返される要素はキーボード フォーカスが設定された要素ですが、古いフォーカス オブジェクトまたは新しいフォーカス オブジェクトが要求をブロックする場合は、要求された要素とは異なる可能性があります。

Focus メソッドを使用して、キーボード フォーカスを Button に設定する例を次に示します。

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
    ' Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton)
End Sub

基底要素クラスの IsKeyboardFocused プロパティでは、要素にキーボード フォーカスがあるかどうかを示す値が取得されます。 基底要素クラスの IsKeyboardFocusWithin プロパティでは、要素またはいずれかの子ビジュアル要素にキーボード フォーカスがあるかどうかを示す値が取得されます。

アプリケーションの起動時に初期フォーカスを設定する場合は、フォーカスを受け取る要素が、アプリケーションによって読み込まれる初期ウィンドウのビジュアル ツリーに含まれていて、FocusableIsVisibletrue に設定されている必要があります。 初期フォーカスは Loaded イベント ハンドラーで設定することをお勧めします。 Dispatcher コールバックは、Invoke または BeginInvoke を呼び出して使用することもできます。

論理フォーカス

論理フォーカスでは、フォーカス範囲内の FocusManager.FocusedElement が参照されます。 フォーカス範囲は、その範囲内の FocusedElement を追跡する要素です。 キーボード フォーカスがフォーカス範囲を離れると、フォーカスがある要素はキーボード フォーカスを失いますが、論理フォーカスは引き続き保持します。 キーボード フォーカスがフォーカス範囲に戻ると、フォーカスがある要素はキーボード フォーカスを取得します。 これにより、キーボード フォーカスが複数のフォーカス範囲間で変更されても、フォーカスがフォーカス範囲に戻ると、そのフォーカス範囲内のフォーカスがある要素はキーボード フォーカスを取り戻すことができます。

アプリケーションでは、複数の要素が論理フォーカスを持つことがありますが、特定のフォーカス範囲で論理フォーカスを持つ要素は 1 つだけに限られます。

キーボード フォーカスを持つ要素は、その要素が属するフォーカス範囲の論理フォーカスを持ちます。

FocusManager の添付プロパティ IsFocusScopetrue に設定することにより、要素を Extensible Application Markup Language (XAML) のフォーカス範囲内にすることができます。 コードでは、SetIsFocusScope を呼び出すことにより、要素をフォーカス範囲内にすることができます。

IsFocusScope 添付プロパティを設定して、StackPanel をフォーカス範囲内にする例を次に示します。

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)

GetFocusScope では、指定した要素のフォーカス範囲が返されます。

既定でフォーカス範囲になる、WPF のクラスは、WindowMenuItemToolBar、および ContextMenu です。

GetFocusedElement を使うと、指定したフォーカス範囲のフォーカスを持つ要素を取得できます。 SetFocusedElement を使うと、指定したフォーカス範囲でフォーカスを持つ要素を設定できます。 SetFocusedElement は、通常、最初にフォーカスを得る要素を設定するために使用します。

フォーカス範囲にフォーカスを持つ要素を設定し、フォーカス範囲のフォーカスを持つ要素を取得する例を次に示します。

// Sets the focused element in focusScope1
// focusScope1 is a StackPanel.
FocusManager.SetFocusedElement(focusScope1, button2);

// Gets the focused element for focusScope 1
IInputElement focusedElement = FocusManager.GetFocusedElement(focusScope1);
' Sets the focused element in focusScope1
' focusScope1 is a StackPanel.
FocusManager.SetFocusedElement(focusScope1, button2)

' Gets the focused element for focusScope 1
Dim focusedElement As IInputElement = FocusManager.GetFocusedElement(focusScope1)

キーボード ナビゲーション

KeyboardNavigation クラスにより、ナビゲーション キーのいずれかが押されたときに、既定のキーボード フォーカスのナビゲーションが実装されます。 ナビゲーション キーは次のとおりです: Tab、Shift + Tab、Ctrl + Tab、Ctrl + Shift + Tab、上方向、下方向、左方向、右方向の各キー。

ナビゲーション コンテナーのナビゲーション動作は、KeyboardNavigation の添付プロパティ TabNavigationControlTabNavigationDirectionalNavigation を設定することにより変更できます。 これらのプロパティは KeyboardNavigationMode 型であり、指定可能な値は ContinueLocalContainedCycleOnce、および None です。 既定値は Continue であり、これは要素がナビゲーション コンテナーではないことを意味します。

複数の MenuItem オブジェクトを使用して Menu を作成する例を次に示します。 Menu では、TabNavigation 添付プロパティが Cycle に設定されます。 Menu 内で Tab キーを使用してフォーカスを変更すると、各要素間をフォーカスが移動し、最後の要素に達すると最初の要素にフォーカスが戻ります。

<Menu KeyboardNavigation.TabNavigation="Cycle">
  <MenuItem Header="Menu Item 1" />
  <MenuItem Header="Menu Item 2" />
  <MenuItem Header="Menu Item 3" />
  <MenuItem Header="Menu Item 4" />
</Menu>
Menu navigationMenu = new Menu();
MenuItem item1 = new MenuItem();
MenuItem item2 = new MenuItem();
MenuItem item3 = new MenuItem();
MenuItem item4 = new MenuItem();

navigationMenu.Items.Add(item1);
navigationMenu.Items.Add(item2);
navigationMenu.Items.Add(item3);
navigationMenu.Items.Add(item4);

KeyboardNavigation.SetTabNavigation(navigationMenu,
    KeyboardNavigationMode.Cycle);
Dim navigationMenu As New Menu()
Dim item1 As New MenuItem()
Dim item2 As New MenuItem()
Dim item3 As New MenuItem()
Dim item4 As New MenuItem()

navigationMenu.Items.Add(item1)
navigationMenu.Items.Add(item2)
navigationMenu.Items.Add(item3)
navigationMenu.Items.Add(item4)

KeyboardNavigation.SetTabNavigation(navigationMenu, KeyboardNavigationMode.Cycle)

フォーカスを操作するための追加の API は、MoveFocusPredictFocus です。

MoveFocus では、アプリケーション内の次の要素にフォーカスが変更されます。 TraversalRequest は、方向を指定するために使用されます。 MoveFocus に渡す FocusNavigationDirection では、FirstLastUpDown など、フォーカスを移動できるさまざまな方向を指定します。

MoveFocus を使用してフォーカスがある要素を変更する例を次に示します。

// Creating a FocusNavigationDirection object and setting it to a
// local field that contains the direction selected.
FocusNavigationDirection focusDirection = _focusMoveValue;

// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);

// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;

// Change keyboard focus.
if (elementWithFocus != null)
{
    elementWithFocus.MoveFocus(request);
}
' Creating a FocusNavigationDirection object and setting it to a
' local field that contains the direction selected.
Dim focusDirection As FocusNavigationDirection = _focusMoveValue

' MoveFocus takes a TraveralReqest as its argument.
Dim request As New TraversalRequest(focusDirection)

' Gets the element with keyboard focus.
Dim elementWithFocus As UIElement = TryCast(Keyboard.FocusedElement, UIElement)

' Change keyboard focus.
If elementWithFocus IsNot Nothing Then
    elementWithFocus.MoveFocus(request)
End If

PredictFocus では、フォーカスが変更された場合にフォーカスを受け取るオブジェクトが返されます。 現時点で PredictFocus によってサポートされているのは、UpDownLeftRight だけです。

フォーカス イベント

キーボード フォーカスに関連するイベントには、PreviewGotKeyboardFocusGotKeyboardFocusPreviewLostKeyboardFocusLostKeyboardFocus があります。 イベントは、Keyboard クラスで添付イベントとして定義されていますが、基底要素クラスで同等のルーティング イベントとしてより簡単にアクセスできます。 イベントの詳細については、「ルーティング イベントの概要」を参照してください。

GotKeyboardFocus は、要素がキーボード フォーカスを取得したときに発生します。 LostKeyboardFocus は、要素がキーボード フォーカスを失ったときに発生します。 PreviewGotKeyboardFocus イベントまたは PreviewLostKeyboardFocusEvent イベントが処理され、Handledtrue に設定されると、フォーカスは変更されなくなります。

GotKeyboardFocus イベント ハンドラーと LostKeyboardFocus イベント ハンドラーを TextBox に添付する例を次に示します。

<Border BorderBrush="Black" BorderThickness="1"
        Width="200" Height="100" Margin="5">
  <StackPanel>
    <Label HorizontalAlignment="Center" Content="Type Text In This TextBox" />
    <TextBox Width="175"
             Height="50" 
             Margin="5"
             TextWrapping="Wrap"
             HorizontalAlignment="Center"
             VerticalScrollBarVisibility="Auto"
             GotKeyboardFocus="TextBoxGotKeyboardFocus"
             LostKeyboardFocus="TextBoxLostKeyboardFocus"
             KeyDown="SourceTextKeyDown"/>
  </StackPanel>
</Border>

TextBox がキーボード フォーカスを取得すると、TextBoxBackground プロパティが LightBlue に変更されます。

private void TextBoxGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    TextBox source = e.Source as TextBox;

    if (source != null)
    {
        // Change the TextBox color when it obtains focus.
        source.Background = Brushes.LightBlue;

        // Clear the TextBox.
        source.Clear();
    }
}
Private Sub TextBoxGotKeyboardFocus(ByVal sender As Object, ByVal e As KeyboardFocusChangedEventArgs)
    Dim source As TextBox = TryCast(e.Source, TextBox)

    If source IsNot Nothing Then
        ' Change the TextBox color when it obtains focus.
        source.Background = Brushes.LightBlue

        ' Clear the TextBox.
        source.Clear()
    End If
End Sub

TextBox がキーボード フォーカスを失うと、TextBoxBackground プロパティが白に戻ります。

private void TextBoxLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    TextBox source = e.Source as TextBox;

    if (source != null)
    {
        // Change the TextBox color when it loses focus.
        source.Background = Brushes.White;

        // Set the  hit counter back to zero and updates the display.
        this.ResetCounter();
    }
}
Private Sub TextBoxLostKeyboardFocus(ByVal sender As Object, ByVal e As KeyboardFocusChangedEventArgs)
    Dim source As TextBox = TryCast(e.Source, TextBox)

    If source IsNot Nothing Then
        ' Change the TextBox color when it loses focus.
        source.Background = Brushes.White

        ' Set the  hit counter back to zero and updates the display.
        Me.ResetCounter()
    End If
End Sub

論理フォーカスに関連するイベントは、GotFocus および LostFocus です。 これらのイベントは、FocusManager で添付イベントとして定義されていますが、FocusManager はで CLR イベント ラッパーは公開されません。 UIElement および ContentElement では、これらのイベントがより使いやすい形で公開されています。

関連項目