焦點概觀
更新:2007 年 11 月
WPF 中有兩個關於焦點的主要概念:鍵盤焦點和邏輯焦點。鍵盤焦點是指收到鍵盤輸入的項目,而邏輯焦點則是指在焦點範圍中具有焦點的項目。本概觀將詳細討論這些概念。認識這些概念的差別,將對建立具有可取得焦點的多個區域的複雜應用程式十分有幫助。
參與焦點管理的主要類別包括 Keyboard 類別、FocusManager 類別以及基底項目類別,例如 UIElement 和 ContentElement。如需基底項目的詳細資訊,請參閱基底項目概觀。
Keyboard 類別主要與鍵盤焦點相關,而 FocusManager 則主要與邏輯焦點相關,但這並不是絕對的分際。具有鍵盤焦點的項目也會具有邏輯焦點,但具有邏輯焦點的項目則不一定會具有鍵盤焦點。這在使用 Keyboard 類別設定具有鍵盤焦點的項目時可明顯看出,因為它也會在項目上設定邏輯焦點。
這個主題包含下列章節。
- 鍵盤焦點
- 邏輯焦點
- 鍵盤巡覽
- 利用程式設計方式巡覽焦點
- 焦點事件
- 相關主題
鍵盤焦點
鍵盤焦點是指目前收到鍵盤輸入的項目。在整個畫面上只能有一個項目具有鍵盤焦點。在 WPF 中,具有鍵盤焦點的項目的 IsKeyboardFocused 將會設成 true。Keyboard 類別上的靜態屬性 FocusedElement 會取得目前具有鍵盤焦點的項目。
為了要讓項目取得鍵盤焦點,基底項目上的 Focusable 和 IsVisible 屬性必須設定為 true。有些如 Panel 基底類別的類別,預設會將 Focusable 設為 false,因此,如果要項目能夠取得鍵盤焦點,就必須將 Focusable 設為 true。
鍵盤焦點可透過與 UI 的使用者互動取得,例如使用 TAB 鍵巡覽到某個項目,或是在某些項目上按一下滑鼠。另外,也可以使用 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);
}
基底項目類別上的 IsKeyboardFocused 屬性會取得值,指出項目是否具有鍵盤焦點。基底項目類別上的 IsKeyboardFocusWithin 會取得值,指出項目或其任何視覺化子項目是否具有鍵盤焦點。
設定應用程式啟動的初始焦點時,收到焦點的項目必須連接至 PresentationSource,而且該項目必須將 Focusable 和 IsVisible 設為 true。設定初始焦點的建議位置是在 Loaded 事件處理常式。Dispatcher 回呼 (Callback) 也可以藉由呼叫 Invoke 或 BeginInvoke 來使用。
邏輯焦點
邏輯焦點是指焦點範圍中的 FocusManager.FocusedElement。焦點範圍是一個項目,會持續追蹤範圍內的 FocusedElement。當鍵盤焦點離開焦點範圍時,具有焦點的項目會失去鍵盤焦點,但卻仍然保有邏輯焦點。當鍵盤焦點返回焦點範圍時,具有焦點的項目將會取得鍵盤焦點。這樣可以讓鍵盤焦點在多個焦點範圍間變更,但可以確保在焦點返回焦點範圍時,焦點範圍中的焦點項目會重新取得鍵盤焦點。
應用程式中可以有多個項目具有邏輯焦點,但在特定焦點範圍中只能有一個項目具有邏輯焦點。
具有鍵盤焦點的項目也具有所屬焦點範圍的邏輯焦點。
項目可以進入焦點範圍內的方法是,在可延伸標記語言 (XAML) 中將 FocusManager 附加屬性 IsFocusScope 設定為 true。在程式碼中,項目則可藉由呼叫 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);
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);
鍵盤巡覽
當按下其中一個巡覽鍵時,KeyboardNavigation 類別會負責實作預設鍵盤焦點巡覽。巡覽鍵有:TAB、SHIFT+TAB、CTRL+TAB、CTRL+SHIFT+TAB 鍵、向上、向下、向左和向右鍵。
巡覽容器的巡覽行為可藉由設定附加 KeyboardNavigation 屬性 TabNavigation、ControlTabNavigation 和 DirectionalNavigation 來變更。這些屬性的型別為 KeyboardNavigationMode,可能的值包括 Continue、Local、Contained、Cycle、Once 和 None。預設值為 Continue,這表示項目不是巡覽容器。
下列範例會建立一個 Menu,其有數個 MenuItem 物件。TabNavigation 附加屬性會設為 Menu 上的 Cycle。在 Menu 內使用 Tab 鍵變更焦點時,焦點會從每個項目移動,當達到最後一個項目時,焦點就會回到第一個項目。
<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);
利用程式設計方式巡覽焦點
其他與焦點相關的 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);
}
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 取得鍵盤焦點時,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();
}
}
當 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();
}
}
與邏輯焦點相關的事件有 GotFocus 和 LostFocus。這些事件會在 FocusManager 上定義為附加屬性,但 FocusManager 不會公開 CLR 事件包裝函式。UIElement 和 ContentElement 能更方便公開這些事件。