포커스 개요
WPF에는 포커스와 관련된 두 가지 주요 개념인 키보드 포커스와 논리적 포커스가 있습니다. 키보드 포커스는 키보드 입력을 받는 요소를 가리키고 논리적 포커스는 포커스 범위 내에서 포커스가 있는 요소를 가리킵니다. 이 개요 부분에서는 이러한 개념에 대해 자세하게 설명합니다. 포커스를 가질 수 있는 영역이 여러 개 포함된 복잡한 응용 프로그램을 만들 경우에는 이 두 개념의 차이를 이해하는 것이 중요합니다.
포커스 관리와 관련된 주요 클래스로는 Keyboard 클래스, FocusManager 클래스 및 UIElement와 ContentElement 같은 기본 요소 클래스가 있습니다. 기본 요소에 대한 자세한 내용은 기본 요소 개요를 참조하십시오.
Keyboard 클래스는 주로 키보드 포커스와 관련되고 FocusManager는 주로 논리적 포커스와 관련되지만 그 사용 영역이 명확하게 구분되는 것은 아닙니다. 키보드 포커스가 있는 요소에는 논리적 포커스도 있지만 논리적 포커스가 있는 요소에는 키보드 포커스가 없을 수 있습니다. Keyboard 클래스를 사용하여 키보드 포커스가 있는 요소를 설정하면 해당 요소에 논리적 포커스도 설정되기 때문에 이 차이를 확실하게 알 수 있습니다.
이 항목에는 다음 단원이 포함되어 있습니다.
- 키보드 포커스
- 논리적 포커스
- 키보드 탐색
- 프로그래밍 방식으로 포커스 탐색
- 포커스 이벤트
- 관련 항목
키보드 포커스
키보드 포커스는 현재 키보드 입력을 받고 있는 요소를 가리킵니다. 전체 데스크톱에서 키보드 포커스를 갖는 요소는 하나만 있을 수 있습니다. WPF에서 키보드 포커스가 있는 요소는 IsKeyboardFocused가 true로 설정됩니다. Keyboard 클래스의 정적 속성 FocusedElement는 현재 키보드 포커스가 있는 요소를 가져옵니다.
요소가 키보드 포커스를 가지려면 기본 요소의 Focusable 속성과 IsVisible 속성이 true로 설정되어야 합니다. Panel 기본 클래스와 같은 일부 클래스는 Focusable이 기본적으로 false로 설정되기 때문에 이러한 요소가 키보드 포커스를 받을 수 있으려면 Focusable을 true로 직접 설정해야 합니다.
키보드 포커스는 탭을 사용하여 요소로 이동하거나 특정 요소를 마우스로 클릭하는 등의 UI의 사용자 상호 작용을 통해 설정할 수 있습니다. 또는 Keyboard 클래스에서 Focus 메서드를 사용하여 프로그래밍 방식으로 키보드 포커스를 설정할 수도 있습니다. Focus 메서드는 지정한 요소에 키보드 포커스를 설정하려고 시도합니다. 이 메서드는 키보드 포커스가 있는 요소를 반환하지만, 이전 포커스 개체나 새 포커스 개체가 요청을 차단한 경우에는 원래 요청한 요소가 아닌 다른 요소가 반환될 수도 있습니다.
다음 예제에서는 Focus 메서드를 사용하여 Button에 키보드 포커스를 설정합니다.
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
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Sets keyboard focus on the first Button in the sample.
Keyboard.Focus(firstButton);
}
기본 요소 클래스의 IsKeyboardFocused 속성은 요소에 키보드 포커스가 있는지 여부를 나타내는 값을 가져옵니다. 기본 요소 클래스의 IsKeyboardFocusWithin 속성은 요소 또는 해당 요소의 시각적 자식 요소 중 하나에 키보드 포커스가 있는지 여부를 나타내는 값을 가져옵니다.
응용 프로그램 시작 시 초기 포커스를 설정하는 경우 포커스를 받을 요소는 응용 프로그램이 로드하는 초기 창의 시각적 트리에 있어야 하고 요소의 Focusable 및 IsVisible은 true로 설정되어 있어야 합니다. 초기 포커스를 설정하기 가장 좋은 위치는 Loaded 이벤트 처리기입니다. Invoke 또는 BeginInvoke를 호출하여 Dispatcher 콜백을 사용할 수도 있습니다.
논리적 포커스
논리적 포커스는 포커스 범위 내의 FocusManager.FocusedElement를 가리킵니다. 포커스 범위는 범위 내에서 FocusedElement를 추적하는 요소입니다. 키보드 포커스가 포커스 범위를 벗어나면 포커스가 있는 요소는 키보드 포커스를 잃지만 논리적 포커스는 그대로 유지합니다. 키보드 포커스가 포커스 범위로 다시 돌아가면 포커스가 있는 요소에 키보드 포커스가 설정됩니다. 이렇게 하면 여러 포커스 범위 사이에서 키보드 포커스가 변경되더라도 포커스 범위에 포커스가 다시 돌아왔을 때 포커스 범위 내에서 포커스가 있는 요소가 키보드 포커스를 다시 받을 수 있습니다.
응용 프로그램에서는 여러 요소가 논리적 포커스를 가질 수 있지만 요소 하나만 논리적 포커스를 가질 수 있는 특정 포커스 범위도 있을 수 있습니다.
키보드 포커스가 있는 요소는 해당 요소가 속한 포커스 범위에서 논리적 포커스도 가집니다.
Extensible Application Markup Language (XAML)에서 FocusManager 연결 속성 IsFocusScope를 true로 설정하면 요소를 포커스 범위로 만들 수 있습니다. 코드에서는 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>
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
GetFocusScope는 지정한 요소의 포커스 범위를 반환합니다.
WPF에서 Window, MenuItem, ToolBar 및 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
Dim focusedElement As IInputElement = FocusManager.GetFocusedElement(focusScope1)
// 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);
키보드 탐색
KeyboardNavigation 클래스는 탐색 키 중 하나를 누른 경우 기본 키보드 포커스 탐색을 구현합니다. 탐색 키로는 Tab, Shift+Tab, Ctrl+Tab, Ctrl+Shift+Tab, UpArrow, DownArrow, LeftArrow 및 RightArrow 키가 있습니다.
탐색 컨테이너의 탐색 동작은 연결된 KeyboardNavigation 속성인 TabNavigation, ControlTabNavigation 및 DirectionalNavigation을 설정하여 변경할 수 있습니다. 이러한 속성의 형식은 KeyboardNavigationMode이며 가능한 값은 Continue, Local, Contained, Cycle, Once 및 None입니다. 기본값은 Continue이며, 이는 요소가 탐색 컨테이너가 아님을 의미합니다.
다음 예제에서는 MenuItem 개체가 여러 개 있는 Menu를 만듭니다. Menu에서 TabNavigation 연결된 속성은 Cycle로 설정되어 있습니다. 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>
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)
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);
프로그래밍 방식으로 포커스 탐색
MoveFocus와 PredictFocus는 포커스로 작업하는 데 사용할 수 있는 추가적인 API입니다.
MoveFocus는 응용 프로그램의 다음 요소로 포커스를 변경합니다. 이때 TraversalRequest는 방향을 지정하는 데 사용됩니다. MoveFocus에 전달되는 FocusNavigationDirection은 First, Last, Up, Down 등 포커스를 이동할 수 있는 여러 방향을 지정합니다.
다음 예제에서는 MoveFocus를 사용하여 포커스가 있는 요소를 변경합니다.
' 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
// 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);
}
PredictFocus는 포커스를 변경할 경우 포커스를 받게 될 개체를 반환합니다. 현재 PredictFocus에는 Up, Down, Left 및 Right만 지원됩니다.
포커스 이벤트
키보드 포커스와 관련된 이벤트로는 PreviewGotKeyboardFocus, GotKeyboardFocus, PreviewLostKeyboardFocus 및 LostKeyboardFocus가 있습니다. 이벤트는 Keyboard 클래스에 연결된 이벤트로 정의되지만 기본 요소 클래스에서 이에 해당되는 라우트된 이벤트로 더 쉽게 액세스할 수 있습니다. 이벤트에 대한 자세한 내용은 라우트된 이벤트 개요를 참조하십시오.
GotKeyboardFocus는 요소가 키보드 포커스를 받을 때 발생하고, LostKeyboardFocus는 요소가 키보드 포커스를 잃을 때 발생합니다. PreviewGotKeyboardFocus 이벤트 또는 PreviewLostKeyboardFocusEvent 이벤트가 처리될 때 Handled가 true로 설정되어 있으면 포커스가 변경되지 않습니다.
다음 예제에서는 TextBox에 GotKeyboardFocus 및 LostKeyboardFocus 이벤트 처리기를 연결합니다.
<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가 키보드 포커스를 받으면 TextBox의 Background 속성이 LightBlue로 변경됩니다.
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
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();
}
}
TextBox가 키보드 포커스를 잃으면 TextBox의 Background 속성이 다시 흰색으로 변경됩니다.
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
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();
}
}
논리적 포커스와 관련된 이벤트로는 GotFocus와 LostFocus가 있습니다. 이 두 이벤트는 FocusManager에 연결된 이벤트로 정의되지만 FocusManager는 CLR 이벤트 래퍼를 노출하지 않습니다. 대신 UIElement 및 ContentElement에서 이러한 이벤트를 노출하므로 더욱 편리합니다.