포커스 개요

WPF에는 포커스와 관련된 두 가지 주요 개념인 키보드 포커스와 논리 포커스가 있습니다. 키보드 포커스는 키보드 입력을 수신하는 요소를 나타내고 논리 포커스는 포커스가 있는 포커스 범위의 요소를 나타냅니다. 이러한 개념은 이 개요에서 자세히 설명합니다. 포커스를 얻을 수 있는 여러 영역이 있는 복잡한 애플리케이션을 작성할 때 이 개념의 차이를 이해하는 것이 중요합니다.

포커스 관리에 참여하는 주요 클래스는 Keyboard 클래스, FocusManager 클래스 및 기본 요소 클래스(예: UIElementContentElement)입니다. 기본 요소에 대한 자세한 내용은 기본 요소 개요를 참조하세요.

Keyboard 클래스는 주로 키보드 포커스와 관련되며 FocusManager는 주로 논리 포커스와 관련되지만 반드시 이와 같이 구분되지는 않습니다. 키보드 포커스가 있는 요소에는 논리 포커스도 있지만, 논리 포커스가 있는 요소에는 키보드 포커스가 없을 수도 있습니다. Keyboard 클래스를 사용하여 키보드 포커스가 있는 요소를 설정할 때 요소에 논리 포커스도 설정하므로 이 점이 명확해집니다.

키보드 포커스

키보드 포커스는 현재 키보드 입력을 수신 중인 요소를 나타냅니다. 키보드 포커스가 있는 전체 데스크탑에는 요소가 하나뿐이어야 합니다. WPF에서 키보드 포커스가 있는 요소에는 true로 설정된 IsKeyboardFocused가 있습니다. Keyboard 클래스의 정적 속성 FocusedElement는 현재 키보드 포커스가 있는 요소를 가져옵니다.

요소에서 키보드 포커스를 가져오게 하려면 기본 요소의 FocusableIsVisible 속성을 true로 설정해야 합니다. Panel 기본 클래스와 같은 일부 클래스에는 기본적으로 false로 설정된 Focusable이 있으므로 이러한 요소에서 키보드 포커스를 가져올 수 있게 하려면 Focusabletrue로 설정해야 합니다.

키보드 포커스를 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 속성은 요소나 시각적 자식 요소 중 하나에 키보드 포커스가 있는지 여부를 표시하는 값을 가져옵니다.

애플리케이션이 시작할 때 초기 포커스를 설정하는 경우 포커스를 받을 요소는 애플리케이션에서 로드한 초기 창의 시각적 트리에 있어야 하며 요소에는 true로 설정된 FocusableIsVisible이 있어야 합니다. 초기 포커스를 설정할 권장 장소는 Loaded 이벤트 처리기에 있습니다. Invoke 또는 BeginInvoke를 호출하여 Dispatcher 콜백을 사용할 수도 있습니다.

논리 포커스

논리 포커스는 포커스 범위의 FocusManager.FocusedElement를 참조합니다. 포커스 범위는 범위 내에서 FocusedElement를 추적하는 요소입니다. 키보드 포커스가 포커스 범위를 벗어나면 포커스된 요소에서 키보드 포커스를 잃지만 논리 포커스는 유지합니다. 키보드 포커스가 포커스 범위로 돌아오면 포커스된 요소가 키보드 포커스를 얻습니다. 그러면 키보드 포커스가 여러 포커스 범위 간에 변경될 수 있지만, 포커스가 포커스 범위로 돌아가면 포커스 범위에 있는 포커스된 요소가 키보드 포커스를 다시 얻습니다.

애플리케이션에 논리 포커스가 있는 요소는 여러 개일 수 있지만, 특정 포커스 범위에 논리 포커스가 있는 요소는 하나뿐일 수 있습니다.

키보드 포커스가 있는 요소에는 요소가 속해 있는 포커스 범위에 대한 논리 포커스가 있습니다.

FocusManager에 연결된 속성 IsFocusScopetrue로 설정하여 XAML(Extensible Application Markup Language)에서 요소를 포커스 범위로 전환할 수 있습니다. 코드에서 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의 클래스는 Window, MenuItem, ToolBarContextMenu입니다.

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, UPARROW, DOWNARROW, LEFTARROW 및 RIGHTARROW 키입니다.

연결된 KeyboardNavigation 속성을 TabNavigation, ControlTabNavigationDirectionalNavigation으로 설정하여 탐색 컨테이너의 탐색 동작을 변경할 수 있습니다. 이러한 속성은 KeyboardNavigationMode 형식이며 가능한 값은 Continue, Local, Contained, Cycle, OnceNone입니다. 기본값은 Continue입니다. 즉, 요소는 탐색 컨테이너가 아닙니다.

다음 예제는 Menu 수가 MenuItem 개체입니다. 합니다 TabNavigation 연결된 속성이로 설정 된 CycleMenu합니다. Menu 내에서 탭 키를 사용하여 포커스를 변경하면 포커스가 각 요소에서 이동하고 마지막 요소에 도달하면 포커스가 첫 번째 요소로 돌아갑니다.

<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은 이동할 수 있는 서로 다른 방향 포커스를 지정합니다(예: First, Last, UpDown).

다음 예제에서는 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에서만 Up, Down, LeftRight를 지원합니다.

포커스 이벤트

키보드 포커스와 관련된 이벤트는 PreviewGotKeyboardFocus, GotKeyboardFocusPreviewLostKeyboardFocus, LostKeyboardFocus입니다. 이벤트는 Keyboard 클래스에 연결된 이벤트로 정의되지만 기본 요소 클래스에서 라우트된 해당 이벤트로 더 쉽게 액세스할 수 있습니다. 이벤트에 대한 자세한 내용은 라우트된 이벤트 개요를 참조하세요.

GotKeyboardFocus는 요소에서 키보드 포커스를 가져오면 발생합니다. LostKeyboardFocus는 요소에서 키보드 포커스가 손실되면 발생합니다. PreviewGotKeyboardFocus 이벤트나 PreviewLostKeyboardFocusEvent 이벤트가 처리되고 Handledtrue로 설정된 경우에는 키보드 포커스가 변경되지 않습니다.

다음 예제에서는 GotKeyboardFocusLostKeyboardFocus 이벤트 처리기를 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

논리 포커스와 관련된 이벤트는 GotFocusLostFocus입니다. 이러한 이벤트는 FocusManager에서 연결된 이벤트로 정의되지만 FocusManager에서는 CLR 이벤트 래퍼를 노출하지 않습니다. UIElementContentElement는 이러한 이벤트를 더욱 편리하게 노출합니다.

참고 항목