焦点概述Focus Overview

WPFWPF 中,有两个与焦点有关的主要概念:键盘焦点和逻辑焦点。In WPFWPF there are two main concepts that pertain to focus: keyboard focus and logical focus. 键盘焦点指接收键盘输入的元素,而逻辑焦点指焦点范围中具有焦点的元素。Keyboard focus refers to the element that receives keyboard input and logical focus refers to the element in a focus scope that has focus. 本概述详细介绍了这些概念。These concepts are discussed in detail in this overview. 对于创建具有多个可获取焦点的区域的复杂应用程序来说,理解这些概念之间的区别非常重要。Understanding the difference in these concepts is important for creating complex applications that have multiple regions where focus can be obtained.

参与焦点管理的主要类包括 Keyboard 类、 FocusManager 类和基元素类,如 UIElementContentElementThe major classes that participate in focus management are the Keyboard class, the FocusManager class, and the base element classes, such as UIElement and ContentElement. 有关基元素的详细信息,请参阅基元素概述For more information about the base elements, see the Base Elements Overview.

Keyboard类主要与键盘焦点相关,并 FocusManager 主要与逻辑焦点相关,但这并不是绝对差别。The Keyboard class is concerned primarily with keyboard focus and the FocusManager is concerned primarily with logical focus, but this is not an absolute distinction. 具有键盘焦点的元素也具有逻辑焦点,但具有逻辑焦点的元素不一定具有键盘焦点。An element that has keyboard focus will also have logical focus, but an element that has logical focus does not necessarily have keyboard focus. 当你使用 Keyboard 类设置具有键盘焦点的元素时,这会很明显,还可以在元素上设置逻辑焦点。This is apparent when you use the Keyboard class to set the element that has keyboard focus, for it also sets logical focus on the element.

键盘焦点Keyboard Focus

键盘焦点指当前正在接收键盘输入的元素。Keyboard focus refers to the element that is currently receiving keyboard input. 在整个桌面上,只能有一个具有键盘焦点的元素。There can be only one element on the whole desktop that has keyboard focus. 在中 WPFWPF ,具有键盘焦点的元素将 IsKeyboardFocused 设置为 trueIn WPFWPF, the element that has keyboard focus will have IsKeyboardFocused set to true. FocusedElement类上的静态属性 Keyboard 获取当前具有键盘焦点的元素。The static property FocusedElement on the Keyboard class gets the element that currently has keyboard focus.

为了使元素能够获取键盘焦点, Focusable IsVisible 基本元素上的和属性必须设置为 trueIn order for an element to obtain keyboard focus, the Focusable and the IsVisible properties on the base elements must be set to true. 某些类,如 Panel 基类, Focusable 默认情况下将设置为 false ; 因此, Focusable true 如果希望此类元素能够获取键盘焦点,则必须将设置为。Some classes, such as the Panel base class, have Focusable set to false by default; therefore, you must set Focusable to true if you want such an element to be able to obtain keyboard focus.

可通过用户与 UIUI 交互(例如,按 Tab 键导航到某个元素或者在某些元素上单击鼠标)来获取键盘焦点。Keyboard focus can be obtained through user interaction with the UIUI, such as tabbing to an element or clicking the mouse on certain elements. 还可以通过使用类上的方法以编程方式获取键盘焦点 Focus KeyboardKeyboard focus can also be obtained programmatically by using the Focus method on the Keyboard class. Focus方法尝试给指定元素的键盘焦点。The Focus method attempts to give the specified element keyboard focus. 返回的元素是具有键盘焦点的元素,如果旧的或新的焦点对象阻止请求,则具有键盘焦点的元素可能不是请求的元素。The returned element is the element that has keyboard focus, which might be a different element than requested if either the old or new focus object block the request.

下面的示例使用 Focus 方法将键盘焦点设置在上 ButtonThe following example uses the Focus method to set keyboard focus on a 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基元素类的属性获取一个值,该值指示元素是否具有键盘焦点。The IsKeyboardFocused property on the base element classes gets a value indicating whether the element has keyboard focus. IsKeyboardFocusWithin基元素类的属性获取一个值,该值指示该元素或它的任何一个可视子元素是否具有键盘焦点。The IsKeyboardFocusWithin property on the base element classes gets a value indicating whether the element or any one of its visual child elements has keyboard focus.

在应用程序启动时设置初始焦点时,接收焦点的元素必须位于应用程序加载的初始窗口的可视化树中,并且该元素必须具有 FocusableIsVisible 将设置为 trueWhen setting initial focus at application startup, the element to receive focus must be in the visual tree of the initial window loaded by the application, and the element must have Focusable and IsVisible set to true. 建议在事件处理程序中设置初始焦点 LoadedThe recommended place to set initial focus is in the Loaded event handler. Dispatcher还可以通过调用或来使用回调 Invoke BeginInvokeA Dispatcher callback can also be used by calling Invoke or BeginInvoke.

逻辑焦点Logical Focus

逻辑焦点引用 FocusManager.FocusedElement 焦点范围内的。Logical focus refers to the FocusManager.FocusedElement in a focus scope. 焦点作用域是跟踪 FocusedElement 其作用域内的的元素。A focus scope is an element that keeps track of the FocusedElement within its scope. 键盘焦点离开焦点范围时,焦点元素会失去键盘焦点,但保留逻辑焦点。When keyboard focus leaves a focus scope, the focused element will lose keyboard focus but will retain logical focus. 键盘焦点返回到焦点范围时,焦点元素会再次获得键盘焦点。When keyboard focus returns to the focus scope, the focused element will obtain keyboard focus. 这使得键盘焦点可在多个焦点范围之间切换,但确保了焦点返回到焦点范围时,焦点范围中的焦点元素重新获得键盘焦点。This allows for keyboard focus to be changed between multiple focus scopes but ensures that the focused element in the focus scope regains keyboard focus when focus returns to the focus scope.

一个应用程序中可以有多个具有逻辑焦点的元素,但在一个特定的焦点范围中只能有一个具有逻辑焦点的元素。There can be multiple elements that have logical focus in an application, but there may only be one element that has logical focus in a particular focus scope.

具有键盘焦点的元素还具有其所属焦点范围的逻辑焦点。An element that has keyboard focus has logical focus for the focus scope it belongs to.

可扩展应用程序标记语言 (XAML)Extensible Application Markup Language (XAML)通过将 FocusManager 附加属性设置 IsFocusScope 为,可以在中将元素转换为焦点范围 trueAn element can be turned into a focus scope in 可扩展应用程序标记语言 (XAML)Extensible Application Markup Language (XAML) by setting the FocusManager attached property IsFocusScope to true. 在代码中,可以通过调用将元素转换为焦点范围 SetIsFocusScopeIn code, an element can be turned into a focus scope by calling SetIsFocusScope.

下面的示例 StackPanel 通过设置附加属性,使成为焦点范围 IsFocusScopeThe following example makes a StackPanel into a focus scope by setting the IsFocusScope attached property.

<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 返回指定元素的焦点范围。GetFocusScope returns the focus scope for the specified element.

WPFWPF默认情况下,焦点范围为、、 Window 和的类 MenuItem ToolBar ContextMenuClasses in WPFWPF which are focus scopes by default are Window, MenuItem, ToolBar, and ContextMenu.

GetFocusedElement 获取指定焦点范围的聚焦元素。GetFocusedElement gets the focused element for the specified focus scope. SetFocusedElement 设置指定焦点范围中的焦点元素。SetFocusedElement sets the focused element in the specified focus scope. SetFocusedElement 通常用于设置初始聚焦元素。SetFocusedElement is typically used to set the initial focused element.

以下示例在焦点范围上设置焦点元素并获取焦点范围的焦点元素。The following example sets the focused element on a focus scope and gets the focused element of a focus scope.

// 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)

键盘导航Keyboard Navigation

KeyboardNavigation类负责在按下一个导航键时实现默认键盘焦点导航。The KeyboardNavigation class is responsible for implementing default keyboard focus navigation when one of the navigation keys is pressed. 导航键包括:Tab、Shift+Tab、Ctrl+Tab、Ctrl+Shift+Tab、向上键、向下键、向左键和向右键。The navigation keys are: TAB, SHIFT+TAB, CTRL+TAB, CTRL+SHIFT+TAB, UPARROW, DOWNARROW, LEFTARROW, and RIGHTARROW keys.

可以通过设置附加的 KeyboardNavigation 属性、和来更改导航容器的导航行为 TabNavigation ControlTabNavigation DirectionalNavigationThe navigation behavior of a navigation container can be changed by setting the attached KeyboardNavigation properties TabNavigation, ControlTabNavigation, and DirectionalNavigation. 这些属性的类型为 KeyboardNavigationMode ,可能的值为 ContinueLocalContainedCycleOnceNoneThese properties are of type KeyboardNavigationMode and the possible values are Continue, Local, Contained, Cycle, Once, and None. 默认值为 Continue ,这意味着该元素不是导航容器。The default value is Continue, which means the element is not a navigation container.

下面的示例创建一个 Menu 具有多个 MenuItem 对象的。The following example creates a Menu with a number of MenuItem objects. TabNavigation 上,附加属性设置为 Cycle MenuThe TabNavigation attached property is set to Cycle on the Menu. 当使用中的 tab 键更改焦点时 Menu ,焦点将从每个元素移动,当到达最后一个元素时,焦点将返回到第一个元素。When focus is changed using the tab key within the Menu, focus will move from each element and when the last element is reached focus will return to the first element.

<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 为 MoveFocusPredictFocusAdditional API to work with focus are MoveFocus and PredictFocus.

MoveFocus 将焦点更改为应用程序中的下一个元素。MoveFocus changes focus to the next element in the application. TraversalRequest用于指定方向。A TraversalRequest is used to specify the direction. FocusNavigationDirection传递的用于 MoveFocus 指定可以移动的不同方向焦点,如 FirstLast UpDownThe FocusNavigationDirection passed to MoveFocus specifies the different directions focus can be moved, such as First, Last, Up and Down.

下面的示例使用 MoveFocus 更改焦点元素。The following example uses MoveFocus to change the focused element.

// 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 returns the object which would receive focus if focus were to be changed. 目前仅 Up 支持、 DownLeftRight PredictFocusCurrently, only Up, Down, Left, and Right are supported by PredictFocus.

焦点事件Focus Events

与键盘焦点相关的事件为 PreviewGotKeyboardFocusGotKeyboardFocusPreviewLostKeyboardFocus LostKeyboardFocusThe events related to keyboard focus are PreviewGotKeyboardFocus, GotKeyboardFocus and PreviewLostKeyboardFocus, LostKeyboardFocus. 事件在类上定义为附加事件 Keyboard ,但更易于访问,作为基元素类上的等效路由事件。The events are defined as attached events on the Keyboard class, but are more readily accessible as equivalent routed events on the base element classes. 有关事件的详细信息,请参阅路由事件概述For more information about events, see the Routed Events Overview.

GotKeyboardFocus 当元素获取键盘焦点时,将引发。GotKeyboardFocus is raised when the element obtains keyboard focus. LostKeyboardFocus 当元素失去键盘焦点时引发。LostKeyboardFocus is raised when the element loses keyboard focus. 如果 PreviewGotKeyboardFocus 处理了事件或 PreviewLostKeyboardFocusEvent 事件并且 Handled 将设置为 true ,则焦点将不会更改。If the PreviewGotKeyboardFocus event or the PreviewLostKeyboardFocusEvent event is handled and Handled is set to true, then focus will not change.

下面的示例将 GotKeyboardFocusLostKeyboardFocus 事件处理程序附加到 TextBoxThe following example attaches GotKeyboardFocus and LostKeyboardFocus event handlers to a 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 更改为 LightBlueWhen the TextBox obtains keyboard focus, the Background property of the TextBox is changed to 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 改回为白色。When the TextBox loses keyboard focus, the Background property of the TextBox is changed back to white.

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

与逻辑焦点相关的事件包括 GotFocusLostFocusThe events related to logical focus are GotFocus and LostFocus. 这些事件定义 FocusManager 为作为附加事件,但不 FocusManager 公开 CLR 事件包装。These events are defined on the FocusManager as attached events, but the FocusManager does not expose CLR event wrappers. UIElementContentElement 更方便地公开这些事件。UIElement and ContentElement expose these events more conveniently.

请参阅See also