Поделиться через


Обзор основной темы

В 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, UPARROW, DOWNARROW, LEFTARROW и RIGHTARROW.

Поведение контейнера навигации можно изменить, задав присоединенные свойства KeyboardNavigation, TabNavigation, ControlTabNavigationи DirectionalNavigation. Эти свойства относятся к типу KeyboardNavigationMode, а возможные значения — Continue, Local, Contained, Cycle, Onceи None. Значение по умолчанию — Continue, что означает, что элемент не является контейнером навигации.

В следующем примере создается Menu с несколькими объектами MenuItem. Присоединенное свойство TabNavigation установлено на Cycle на Menu. При изменении фокуса с помощью клавиши tab в 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 возвращает объект, который получит фокус при изменении фокуса. В настоящее время только Up, Down, Leftи Right поддерживаются PredictFocus.

Фокусные события

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

См. также