焦點概觀

在 WPF 中,有兩個主要概念與焦點有關:鍵盤焦點和邏輯焦點。 鍵盤焦點是指接收鍵盤輸入的項目,邏輯焦點是指焦點範圍中具有焦點的項目。 本概觀會詳細討論這些概念。 了解這些概念的差異對建立複雜的應用程式很重要,這些應用程式有多個可取得焦點的區域。

參與焦點管理的主要類別是 Keyboard 類別、 FocusManager 類別和基底元素類別,例如 UIElementContentElement 。 如需基底項目的詳細資訊,請參閱基底項目概觀

類別 Keyboard 主要與鍵盤焦點有關,而 FocusManager 主要涉及邏輯焦點,但這不是絕對的區別。 具有鍵盤焦點的項目也會有邏輯焦點,但具有邏輯焦點的項目不一定有鍵盤焦點。 當您使用 Keyboard 類別來設定具有鍵盤焦點的元素時,這很明顯,因為它也會在 元素上設定邏輯焦點。

鍵盤焦點

鍵盤焦點是指目前接收鍵盤輸入的項目。 整個桌面只能有一個項目有鍵盤焦點。 在 WPF 中,具有鍵盤焦點的專案將會 IsKeyboardFocused 設定為 true 。 類別上的 Keyboard 靜態屬性 FocusedElement 會取得目前具有鍵盤焦點的專案。

為了讓專案取得鍵盤焦點, Focusable 基底元素上的 和 IsVisible 屬性必須設定為 true 。 某些類別,例如 Panel 基類,預設已 Focusable 設定為 false ,因此,如果您想要讓這類元素能夠取得鍵盤焦點,則必須將 設定 Focusabletrue 為 。

鍵盤焦點可以透過使用者與 UI 的互動取得,例如將索引標籤移至元素,或按一下特定元素上的滑鼠。 您也可以使用 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基底專案類別上的 屬性會取得值,指出專案或其任何一個視覺子項目是否具有鍵盤焦點。

在應用程式啟動時設定初始焦點時,要接收焦點的專案必須位於應用程式載入之初始視窗的視覺化樹狀結構中,而且元素必須具有 FocusableIsVisible 設定為 true 。 設定初始焦點的建議位置是在事件處理常式中 LoadedDispatcher呼叫 或 BeginInvoke 也可以使用回呼 Invoke

邏輯焦點

邏輯焦點是指 FocusManager.FocusedElement 焦點範圍中的 。 焦點範圍是可追蹤其範圍內 的專案 FocusedElement 。 當鍵盤焦點離開焦點範圍時,焦點項目就會失去鍵盤焦點,但卻仍然保有邏輯焦點。 當鍵盤焦點回到焦點範圍時,焦點項目就會取得鍵盤焦點。 這讓鍵盤焦點能在多個焦點範圍間變更,但確保當焦點回到集點範圍內時,焦點範圍內的焦點項目會重新取得鍵盤焦點。

應用程式中可以有多個具有邏輯焦點的項目,但特定的焦點範圍中只能有一個有邏輯焦點的項目。

具有鍵盤焦點的項目有其所屬焦點範圍的邏輯焦點。

將附加屬性 IsFocusScopetrue 設定 FocusManager 為 ,即可在 Extensible Application Markup Language (XAML) 中將專案轉換成焦點範圍。 在程式碼中,專案可以藉由呼叫 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 、、 MenuItemToolBarContextMenu

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 屬性 TabNavigationControlTabNavigationDirectionalNavigation 來變更巡覽容器的流覽行為。 這些屬性的類型和 KeyboardNavigationMode 可能的值為 Continue 、、 LocalContainedCycleOnceNone 。 預設值為 Continue ,這表示專案不是導覽容器。

下列範例會 Menu 建立具有多個 MenuItem 物件的 。 附加 TabNavigation 屬性在 上 Menu 設定為 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>
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用來指定方向。 FocusNavigationDirection傳遞以 MoveFocus 指定可以移動不同方向的焦點,例如 FirstLastUpDown

下列範例會使用 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 會傳回物件,如果焦點要變更,則會接收焦點。 目前只有 UpDownLeftRight 受到 PredictFocus 支援。

焦點事件

與鍵盤焦點相關的事件為 PreviewGotKeyboardFocusGotKeyboardFocusPreviewLostKeyboardFocusLostKeyboardFocus 。 事件定義為 類別上的 Keyboard 附加事件,但更容易存取為基底專案類別上的對等路由事件。 如需事件的詳細資訊,請參閱路由事件概觀

GotKeyboardFocus 當專案取得鍵盤焦點時,會引發 。 LostKeyboardFocus 當專案失去鍵盤焦點時,會引發 。 PreviewGotKeyboardFocus如果事件或 PreviewLostKeyboardFocusEvent 事件已處理並 Handled 設定為 true ,則焦點不會變更。

下列範例會將 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取得鍵盤焦點時, BackgroundTextBox 屬性會變更為 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當 失去鍵盤焦點時, BackgroundTextBox 屬性會變更回白色。

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 更方便地公開這些事件。

另請參閱