焦點概觀
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 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 事件處理常式中。 呼叫 Invoke 或 BeginInvoke ,也可以使用 Dispatcher 回呼。
邏輯焦點
邏輯焦點是指焦點範圍中的 FocusManager.FocusedElement。 焦點範圍是追蹤其範圍內 FocusedElement 的元素。 當鍵盤焦點離開焦點範圍時,焦點項目就會失去鍵盤焦點,但卻仍然保有邏輯焦點。 當鍵盤焦點回到焦點範圍時,焦點項目就會取得鍵盤焦點。 這讓鍵盤焦點能在多個焦點範圍間變更,但確保當焦點回到集點範圍內時,焦點範圍內的焦點項目會重新取得鍵盤焦點。
應用程式中可以有多個具有邏輯焦點的項目,但特定的焦點範圍中只能有一個有邏輯焦點的項目。
具有鍵盤焦點的項目有其所屬焦點範圍的邏輯焦點。
將 FocusManager 附加屬性 IsFocusScope 設定為 true
,即可在 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、 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,這表示元素不是導覽容器。
下列範例會建立具有多個 MenuItem 物件的 Menu。 TabNavigation 附加屬性會設定為 Menu 上的 Cycle。 使用 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 來指定方向。 傳遞至 MoveFocus 的 FocusNavigationDirection 會指定可以移動的不同方向焦點,例如 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 和 PreviewLostKeyboardFocusLostKeyboardFocus。 事件在 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 失去鍵盤焦點時, TextBox 的 Background 屬性會變更回白色。
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 能更方便地公開這些事件。