Общие сведения о фокусе

Существует два основных понятия, относящихся к фокусу в WPF: фокус клавиатуры и логический фокус. Фокус клавиатуры ссылается на элемент, получающий ввод с клавиатуры, а логический фокус ссылается на элемент в области фокуса, имеющий фокус. Эти понятия подробно освещаются в этом обзоре. Важно понимать разницу между этими понятиями, чтобы создавать сложные приложения с несколькими областями, в которых можно получить фокус.

Основные классы, участвующие в управлении фокусом: класс Keyboard, класс FocusManager, а также классы базовых элементов, такие как UIElement и ContentElement. Дополнительные сведения о базовых элементах см. в разделе Общие сведения о базовых элементах.

Класс Keyboard занимается в основном фокусом клавиатуры, а FocusManager — логическим фокусом, однако это разделение нельзя назвать абсолютным. Элемент, имеющий фокус клавиатуры, также будет иметь логический фокус, но элемент, имеющий логический фокус, не обязательно будет иметь фокус клавиатуры. Это видно при использовании класса Keyboard для настройки элемента с фокусом клавиатуры, поскольку он также задает логический фокус элемента.

Фокус клавиатуры

Фокус клавиатуры относится к элементу, получающему ввод с клавиатуры. На всем рабочем столе может быть только один элемент, в котором находится фокус клавиатуры. В WPF элемент с фокусом клавиатуры должен использовать для параметра IsKeyboardFocused значение true. Статическое свойство FocusedElement в классе Keyboard получает элемент, для которого в настоящее время имеется фокус клавиатуры.

Чтобы элемент получил фокус клавиатуры, свойства Focusable и IsVisible базовых элементов должны быть заданы со значением true. Некоторые классы, такие как базовый класс Panel, используют Focusable со значением false по умолчанию. Таким образом, необходимо задать для Focusable значение true, если требуется, чтобы такой элемент мог получать фокус клавиатуры.

Фокус клавиатуры можно получить с помощью взаимодействия пользователя с пользовательским интерфейсом, например перехода к элементу или щелчка мышью определенных элементов. Фокус клавиатуры также можно получить программно, используя метод Focus в классе Keyboard. Метод 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 в классах базовых элементов получает значение, указывающее, имеет ли элемент или какой-либо из его визуальных дочерних элементов фокус клавиатуры.

При настройке начального фокуса при запуске приложения элемент, который должен получить фокус, должен находиться в визуальном дереве первоначального окна, загружаемого приложением, и этот элемент должен иметь параметры Focusable и IsVisible, для которых задано значение true. Рекомендуется поместить исходный фокус в обработчик событий Loaded. Для обратного вызова Dispatcher также можно использовать вызов Invoke или BeginInvoke.

Логический фокус

Логический фокус ссылается на FocusManager.FocusedElement в области фокуса. Область фокуса представляет собой элемент, который отслеживает FocusedElement в своей области. Когда фокус клавиатуры покидает область фокуса, элемент с фокусом теряет фокус клавиатуры, но сохраняет логический фокус. При возвращении фокуса клавиатуры в область фокуса элемент с логическим фокусом получит фокус клавиатуры. Это позволяет переносить фокус клавиатуры между несколькими областями фокуса, но гарантирует, что элемент с фокусом в области фокуса останется элементом с фокусом клавиатуры, когда фокус вернется в эту область.

В приложении может быть несколько элементов, имеющих логический фокус, но в отдельной области фокуса может быть только один элемент, имеющий логический фокус.

Элемент, имеющий фокус клавиатуры, имеет логический фокус для области фокуса, которой он принадлежит.

Элемент можно превратить в область фокуса в языке XAML, задав для FocusManagerприсоединенного свойства IsFocusScope значение true. В коде элемент можно преобразовать в область фокуса, вызвав SetIsFocusScope.

В следующем примере элемент StackPanel преобразуется в область фокуса за счет настройки присоединенного свойства IsFocusScope.

<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, 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
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: TabNavigation, ControlTabNavigation и DirectionalNavigation. Эти свойства относятся к типу KeyboardNavigationMode; возможные значения: Continue, Local, Contained, Cycle,Once и None. По умолчанию используется значение Continue, то есть, элемент не является контейнером навигации.

В следующем примере создается Menu с несколькими объектами MenuItem. Для присоединенного свойства TabNavigation задано значение Cycle в Menu. При изменении фокуса с помощью клавиши табуляции в 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 для работы с фокусом: MoveFocus и PredictFocus.

MoveFocus переносит фокус на следующий элемент в приложении. Используется TraversalRequest для указания направления. FocusNavigationDirection, переданный в MoveFocus, указывает на различные направления, в которых может перемещаться фокус: например, First, Last, Up и Down.

В следующем примере 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, Left и Right.

События фокуса

События, связанные с фокусом клавиатуры: PreviewGotKeyboardFocus, GotKeyboardFocus и PreviewLostKeyboardFocus, LostKeyboardFocus. События определяются как вложенные в классе Keyboard, однако они более доступны как эквивалентные перенаправленные события в классах базовых элементов. Дополнительные сведения о событиях см. в разделе Общие сведения о перенаправленных событиях.

Происходит GotKeyboardFocus, если элемент получает фокус клавиатуры. Происходит LostKeyboardFocus, когда элемент теряет фокус клавиатуры. Если обрабатывается событие PreviewGotKeyboardFocus или PreviewLostKeyboardFocusEvent, а для Handled задано значение true, фокус не изменяется.

В следующем примере выполняется присоединение обработчиков событий 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 получает фокус клавиатуры, свойство Background для TextBox изменится на 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, цвет Background элемента TextBox изменяется обратно на белый.

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 предоставляют эти события более удобным способом.

См. также