共用方式為


輸入概觀

Windows Presentation Foundation (WPF) 子系統提供一個功能強大的 API,以取得來自各種裝置的輸入,包括滑鼠、鍵盤、觸控和手寫筆。 本主題描述 WPF 提供的服務,以及說明輸入系統的架構。

輸入 API

在基礎元素類別上發現主要輸入 API 暴露:UIElementContentElementFrameworkElementFrameworkContentElement。 如需基底項目的詳細資訊,請參閱基底項目概觀。 這些類別提供按鍵動作、滑鼠按鈕、滑鼠滾輪、滑鼠移動、焦點管理和滑鼠捕捉等相關輸入事件的功能。 將輸入 API 放在基礎元素上,而不是將所有輸入事件視為服務,輸入架構可讓輸入事件成為 UI 中的特定物件來源,以及支援事件路由配置,進而讓多個元素可以處理單一輸入事件。 許多輸入事件都有一組與其建立關聯的事件。 例如,向下鍵事件與 KeyDownPreviewKeyDown 事件相關聯。 這些事件的差異在於如何將它們路由傳送至目標項目。 預覽事件會從根項目到目標項目往下瀏覽通道項目樹狀結構。 事件反昇事件會從目標項目往上反昇到根項目。 此概觀和路由事件概觀中稍後會更詳細討論 WPF 中的事件路由。

鍵盤和滑鼠類別

除了基礎元素類別上的輸入 API 之外,Keyboard 類別和 Mouse 類別也提供額外的 API 來使用鍵盤和滑鼠輸入。

Keyboard 類別上的輸入 API 範例為 Modifiers 屬性,其會傳回目前按下的 ModifierKeys,以及 IsKeyDown 方法,其會判斷是否按下指定按鍵。

下列範例會使用 GetKeyStates 方法來判斷 Key 是否處於關閉狀態。

// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison.
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
    btnNone.Background = Brushes.Red;
}
' Uses the Keyboard.GetKeyStates to determine if a key is down.
' A bitwise AND operation is used in the comparison. 
' e is an instance of KeyEventArgs.
If (Keyboard.GetKeyStates(Key.Return) And KeyStates.Down) > 0 Then
    btnNone.Background = Brushes.Red

Mouse 類別上的輸入 API 範例為 MiddleButton,其會取得滑鼠中鍵的狀態,而 DirectlyOver 會取得滑鼠指標目前已結束的元素。

下列範例會判斷滑鼠上的 LeftButton 是否處於 Pressed 狀態。

if (Mouse.LeftButton == MouseButtonState.Pressed)
{
    UpdateSampleResults("Left Button Pressed");
}
If Mouse.LeftButton = MouseButtonState.Pressed Then
    UpdateSampleResults("Left Button Pressed")
End If

本概觀會更詳細地涵蓋 MouseKeyboard 類別。

手寫筆輸入

WPF 已整合對 Stylus 的支援。 Stylus 是由於平板電腦而變得熱門的手寫筆輸入。 WPF 應用程式可以使用滑鼠 API 來將手寫筆視為滑鼠,但是 WPF 也會公開手寫筆裝置抽象概念,其使用類似鍵盤和滑鼠的模型。 所有手寫筆相關的 API 都會包含 "Stylus" 一字。

因為手寫筆可以當作滑鼠,所以只支援滑鼠輸入的應用程式仍然可以自動取得某種程度的手寫筆支援。 以這種方式使用手寫筆時,應用程式可以處理適當的手寫筆事件,然後處理對應的滑鼠事件。 此外,還可以透過手寫筆裝置抽象概念來取得筆跡輸入這類較高階服務。 如需將筆跡作為輸入的詳細資訊,請參閱筆跡入門

事件路由

FrameworkElement 可以包含其他元素作為其內容模型中的子元素,形成元素樹狀結構。 在 WPF 中,父元素可以透過處理事件,來參與導向至其子元素或其他子系的輸入。 這特別適用於透過較小的控制項來建置控制項,即稱為「控制項組合」或「組合」的程序。如需項目樹狀結構以及項目樹狀結構與事件路由間之關聯性的詳細資訊,請參閱 WPF 中的樹狀結構

事件路由是將事件轉遞至多個項目的程序,因此,沿著路由的特定物件或項目可以選擇將重大回應提供給不同項目可能設為來源的事件 (透過處理)。 路由事件使用三種路由機制中的其中一種︰直接、事件反昇和通道。 在直接路由中,來源項目是唯一收到通知的項目,而且不會將事件路由傳送至任何其他項目。 不過,直接路由事件仍會提供只有路由事件才能使用的一些額外功能,而不是標準 CLR 事件。 事件反昇處理項目樹狀結構的方式是先通知將事件設為來源的項目,接著通知父項目,依此類推。 通道會從項目樹狀結構的根項目開始,然後往下進行,並結束於原始來源項目。 如需路由事件的詳細資訊,請參閱路由事件概觀

WPF 輸入事件一般會成對出現,其包含通道事件和事件反昇事件。 通道事件與事件反昇事件的區別在於 "Preview" 前置詞。 例如,PreviewMouseMove 是滑鼠移動事件的通道版本,MouseMove 是此事件的事件反昇版本。 此事件配對是一項在元素層級實作的慣例,而不是 WPF 事件系統的一項固有功能。 如需詳細資訊,請參閱路由事件概觀中的<WPF 輸入事件>一節。

處理輸入事件

若要接收項目的輸入,事件處理常式必須與該特定事件建立關聯。 在 XAML 中,這相當簡單:您可以參考事件名稱,作為將接聽此事件之元素的屬性。 然後,您可以根據委派,將屬性的值設定為您所定義之事件處理常式的名稱。 事件處理常式必須在 C# 這類程式碼中撰寫,而且可以包含在程式碼後置檔案中。

作業系統報告在鍵盤焦點位於項目時所發生的按鍵動作時,會發生鍵盤事件。 滑鼠和手寫筆事件各分為兩個分類︰報告相對於項目之指標位置變更的事件,以及報告裝置按鈕狀態變更的事件。

鍵盤輸入事件範例

下列範例會接聽按下向左鍵作業。 隨即會建立具有 StackPanelButton。 接聽是否按下向左鍵的事件處理常式會附加至 Button 執行個體。

範例的第一個區段會建立 StackPanelButton,並附加 KeyDown 的事件處理常式。

<StackPanel>
  <Button Background="AliceBlue"
          KeyDown="OnButtonKeyDown"
          Content="Button1"/>
</StackPanel>
// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();

// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";

// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);

// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);
' Create the UI elements.
Dim keyboardStackPanel As New StackPanel()
Dim keyboardButton1 As New Button()

' Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue
keyboardButton1.Content = "Button 1"

' Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1)

' Attach event handler.
AddHandler keyboardButton1.KeyDown, AddressOf OnButtonKeyDown

第二個區段是使用程式碼所撰寫,並定義事件處理常式。 按下向左鍵且 Button 具有鍵盤焦點時,處理常式就會執行,並變更 BackgroundButton 色彩。 如果按下按鍵,但不是向左鍵,則 BackgroundButton 色彩會變更回其起始色彩。

private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
    Button source = e.Source as Button;
    if (source != null)
    {
        if (e.Key == Key.Left)
        {
            source.Background = Brushes.LemonChiffon;
        }
        else
        {
            source.Background = Brushes.AliceBlue;
        }
    }
}
Private Sub OnButtonKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    Dim source As Button = TryCast(e.Source, Button)
    If source IsNot Nothing Then
        If e.Key = Key.Left Then
            source.Background = Brushes.LemonChiffon
        Else
            source.Background = Brushes.AliceBlue
        End If
    End If
End Sub

滑鼠輸入事件範例

在下列範例中,當滑鼠指標進入 Background 時,ButtonButton 色彩會變更。 當滑鼠離開 Background 時,會還原 Button 色彩。

範例的第一個區段會建立 StackPanelButton 控制項,並將 MouseEnterMouseLeave 事件的事件處理常式附加至 Button

<StackPanel>
  <Button Background="AliceBlue"
          MouseEnter="OnMouseExampleMouseEnter"
          MouseLeave="OnMosueExampleMouseLeave">Button
          
  </Button>
</StackPanel>
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();

// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";

// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);

// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);
' Create the UI elements.
Dim mouseMoveStackPanel As New StackPanel()
Dim mouseMoveButton As New Button()

' Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue
mouseMoveButton.Content = "Button"

' Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton)

' Attach event handler.
AddHandler mouseMoveButton.MouseEnter, AddressOf OnMouseExampleMouseEnter
AddHandler mouseMoveButton.MouseLeave, AddressOf OnMosueExampleMouseLeave

此範例的第二個區段是使用程式碼所撰寫,並定義事件處理常式。 當滑鼠進入 Button 時,BackgroundButton 色彩會變更為 SlateGray。 當滑鼠離開 Button 時,BackgroundButton 色彩會變更回 AliceBlue

private void OnMouseExampleMouseEnter(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.SlateGray;
    }
}
Private Sub OnMouseExampleMouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs)
    ' Cast the source of the event to a Button.
    Dim source As Button = TryCast(e.Source, Button)

    ' If source is a Button.
    If source IsNot Nothing Then
        source.Background = Brushes.SlateGray
    End If
End Sub
private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.AliceBlue;
    }
}
Private Sub OnMosueExampleMouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs)
    ' Cast the source of the event to a Button.
    Dim source As Button = TryCast(e.Source, Button)

    ' If source is a Button.
    If source IsNot Nothing Then
        source.Background = Brushes.AliceBlue
    End If
End Sub

文字輸入

TextInput 事件可讓您以與裝置無關的方式接聽文字輸入。 鍵盤是文字輸入的主要方法,但是語音、手寫和其他輸入裝置也可以產生文字輸入。

針對鍵盤輸入,WPF 會先傳送適當的 KeyDown/KeyUp 事件。 如果未處理這些事件,而且按鍵是文字 (而非方向鍵或功能鍵這類控制鍵),則會引發 TextInput 事件。 KeyDown / KeyUpTextInput 事件之間不一定有簡單的一對一對應,因為多個按鍵輸入可以產生文字輸入的單一字元,而單一按鍵輸入可以產生多個字元字串。 這特別適用於中文、日文和韓文這類語言,而其使用 IME 來產生其對應字母的數千個可能字元。

當 WPF 傳送 KeyUp/KeyDown 事件時,如果按鍵輸入可能成為 Key 事件的一部分 (例如按下 ALT+S),Key.System 會設定為 TextInput。 這可讓 KeyDown 事件處理常式中的程式碼檢查 Key.System,如果找到,則會保留後續引發 TextInput 事件的處理常式處理。 在這些情況下,TextCompositionEventArgs 引數的各種屬性可以用來判斷原始按鍵輸入。 同樣地,如果 IME 為使用中,Key 具有 Key.ImeProcessed 值,且 ImeProcessedKey 會提供一或多個原始按鍵輸入。

下列範例會定義 Click 事件的處理常式,以及 KeyDown 事件的處理常式。

程式碼或標記的第一個區段會建立使用者介面。

<StackPanel KeyDown="OnTextInputKeyDown">
  <Button Click="OnTextInputButtonClick"
          Content="Open" />
  <TextBox> . . . </TextBox>
</StackPanel>
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";

// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);

// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);
' Create the UI elements.
Dim textInputStackPanel As New StackPanel()
Dim textInputeButton As New Button()
Dim textInputTextBox As New TextBox()
textInputeButton.Content = "Open"

' Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton)
textInputStackPanel.Children.Add(textInputTextBox)

' Attach event handlers.
AddHandler textInputStackPanel.KeyDown, AddressOf OnTextInputKeyDown
AddHandler textInputeButton.Click, AddressOf OnTextInputButtonClick

程式碼的第二個區段包含事件處理常式。

private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
    {
        handle();
        e.Handled = true;
    }
}

private void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
    handle();
    e.Handled = true;
}

public void handle()
{
    MessageBox.Show("Pretend this opens a file");
}
Private Sub OnTextInputKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    If e.Key = Key.O AndAlso Keyboard.Modifiers = ModifierKeys.Control Then
        handle()
        e.Handled = True
    End If
End Sub

Private Sub OnTextInputButtonClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
    handle()
    e.Handled = True
End Sub

Public Sub handle()
    MessageBox.Show("Pretend this opens a file")
End Sub

因為輸入事件會往上反昇事件路由,所以不論哪個元素具有鍵盤焦點,StackPanel 都會收到輸入。 TextBox 控制項會先收到通知,且只有在 OnTextInputKeyDown 未處理輸入時,才會呼叫 TextBox 處理常式。 如果使用 PreviewKeyDown 事件而非 KeyDown 事件,則會先呼叫 OnTextInputKeyDown 處理常式。

在此範例中,處理邏輯會撰寫兩次:一次針對 CTRL+O,一次則是針對按鈕的 Click 事件。 這可以使用命令進行簡化,而不是直接處理輸入事件。 這個概觀和命令概觀討論命令。

觸控和操作

Windows 7 作業系統中的新硬體和 API 讓應用程式可以同時接收來自多個觸控的輸入。 WPF 可讓應用程式在發生觸控時引發事件,以透過回應其他輸入 (例如滑鼠或鍵盤) 的類似方式來偵測和回應觸控。

WPF 會在觸控發生時公開兩種類型的事件︰觸控事件和操作事件。 觸控事件提供觸控式螢幕上每根手指的未經處理資料和其移動。 操作事件會將輸入解譯成特定動作。 本節討論這兩種類型的事件。

必要條件

您需要下列元件,才能開發回應觸控的應用程式。

  • Visual Studio 2010。

  • Windows 7。

  • 支援 Windows Touch 的觸控式螢幕這類裝置。

詞彙

討論觸控時,會使用下列詞彙。

  • 觸控是 Windows 7 可辨識的一種使用者輸入類型。 通常,將手指放在觸控式螢幕上,就會起始觸控。 請注意,如果裝置只會將手指的位置和移動轉換為滑鼠輸入,則膝上型電腦上常見的觸控板這類裝置不支援觸控。

  • 多點觸控是同時從多點發生的觸控。 Windows 7 和 WPF 支援多點觸控。 只要 WPF 文件中討論觸控,這些概念就適用多點觸控。

  • 將觸控解譯為套用至物件的實體動作時,會發生操作。 在 WPF 中,操作事件會將輸入解譯為平移、擴充或旋轉操作。

  • touch device 所代表的裝置會產生觸控式螢幕上的一根手指這類觸控輸入。

回應觸控的控制項

如果控制項具有可捲動到檢視外部的內容,則將手指拖曳過該控制項即可捲動下列控制項。

ScrollViewer 會定義 ScrollViewer.PanningMode 附加屬性,讓您指定是否要針對觸控移動瀏覽啟用水平、垂直或兩者或兩者皆不。 ScrollViewer.PanningDeceleration 屬性會指定當使用者的手指從觸控螢幕上抬起時向下捲動速度變慢的速度。 ScrollViewer.PanningRatio 附加屬性會指定捲動位移的比率,以轉譯操作位移。

觸控事件

基礎類別 UIElementUIElement3DContentElement 會定義您可以訂閱的事件,讓應用程式回應觸控。 您的應用程式將觸控解譯為非操作物件的動作時,觸控事件十分有用。 例如,可讓使用者使用一或多根手指繪製的應用程式訂閱觸控事件。

不論定義類別為何,所有這三個類別都會定義下列行為類似的事件。

與鍵盤和滑鼠事件類似,觸控事件都是路由事件。 開頭為 Preview 的事件是通道事件,而開頭為 Touch 的事件是事件反昇事件。 如需路由事件的詳細資訊,請參閱路由事件概觀。 處理這些事件時,您可以藉由呼叫 GetTouchPointGetIntermediateTouchPoints 方法,取得輸入相對於任何元素的位置。

若要了解觸控事件之間的互動,請考慮在使用者將一根手指放在項目上,並將手指移至項目中,然後將手指移開項目。 下圖顯示事件反昇事件的執行 (為求簡化,會省略通道事件)。

觸控事件的順序。 觸控事件

下列清單描述上圖中的事件順序。

  1. 當使用者將手指放在元素上時,就會發生 TouchEnter 事件。

  2. TouchDown 事件會發生一次。

  3. 使用者將手指移入元素內時,TouchMove 事件會發生多次。

  4. 使用者將手指移開元素時,TouchUp 事件會發生一次。

  5. TouchLeave 事件會發生一次。

使用兩根以上的手指時,每根手指都會發生這些事件。

操作事件

如果應用程式可讓使用者操作物件,則 UIElement 類別會定義操作事件。 與只會報告觸控位置的觸控事件不同,操作事件會報告輸入解譯方式。 操作有三種類型:平移、擴充和旋轉。 下列清單描述如何叫用這三種類型的操作。

  • 將手指放在物件上,然後將手指移過觸控式螢幕,以叫用轉譯操作。 這通常會移動物件。

  • 將兩根手指放在物件上,然後拉近兩根手指或分開兩根手指,以叫用擴充操作。 這通常會調整物件大小。

  • 將兩根手指放在物件上,然後旋轉這兩根手指,以叫用旋轉操作。 這通常會旋轉物件。

可以同時進行多種類型的操作。

當您讓物件回應操作時,可以讓物件看起來像有慣性。 這可讓您的物件模擬真實世界。 例如,當您在桌上推動書本時,如果推得夠用力,則在放開書本之後,書本還會繼續移動。 WPF 可讓您透過在使用者的手指放開物件之後引發操作事件,來模擬此行為。

如需如何建立可讓使用者移動、調整大小和旋轉物件的應用程式的資訊,請參閱逐步解說:建立您的第一個觸控應用程式

UIElement 會定義下列操作事件。

UIElement 預設不會收到這些操作事件。 若要在 UIElement 上接收操作事件,請將 UIElement.IsManipulationEnabled 設定為 true

操作事件的執行路徑

請考慮使用者「擲回」物件的情況。 使用者將手指放在物件上,並將手指移過觸控式螢幕的一小段距離,然後在移動時移開手指。 這樣的結果是會移動使用者手指下方的物件,並在使用者移開手指之後繼續移動。

下圖顯示操作事件的執行路徑以及每個事件的重要資訊。

操作事件的順序。 操作事件

下列清單描述上圖中的事件順序。

  1. 當使用者將手指放在物件上時,就會發生 ManipulationStarting 事件。 除此之外,此事件可讓您設定 ManipulationContainer 屬性。 在後續事件中,操作的位置會與 ManipulationContainer 相對。 在 ManipulationStarting 以外的事件中,此屬性是唯讀的,因此 ManipulationStarting 事件是唯一可以設定此屬性的時間。

  2. 接著會發生 ManipulationStarted 事件。 此事件會報告操作的原點。

  3. 當使用者的手指在觸控螢幕上移動時,ManipulationDelta 事件會發生多次。 DeltaManipulation 類別的 ManipulationDeltaEventArgs 屬性會報告操作是否解譯為移動、展開或轉譯。 這是您執行大部分物件操作工作的位置。

  4. 當使用者的手指失去與物件的接觸時,就會發生 ManipulationInertiaStarting 事件。 此事件可讓您指定慣性期間的操作減速。 因此,如果選擇的話,您的物件可以模擬不同的實體空間或屬性。 例如,假設應用程式有兩個物件代表真實世界中的項目,而且其中一個比另一個重。 您可以將較重的物件減速,使其比較輕的物件更快。

  5. ManipulationDelta 事件發生多次,因為慣性發生。 請注意,使用者手指移過觸控式螢幕時,以及 WPF 模擬慣性時,都會發生此事件。 換句話說,ManipulationDelta 發生在 ManipulationInertiaStarting 事件前後。 ManipulationDeltaEventArgs.IsInertial 屬性會報告 ManipulationDelta 事件是否發生在慣性期間,因此您可以根據屬性的值檢查該屬性並執行不同的動作。

  6. 當操作和任何慣性結束時,就會發生 ManipulationCompleted 事件。 也就是說,在所有 ManipulationDelta 事件發生之後,就會發生 ManipulationCompleted 事件,以表示操作已完成。

UIElement 也會定義 ManipulationBoundaryFeedback 事件。 在 ReportBoundaryFeedback 事件中呼叫 ManipulationDelta 方法時,就會發生此事件。 ManipulationBoundaryFeedback 事件可讓應用程式或元件在物件到達界限時提供視覺化回饋意見。 例如,Window 類別會處理 ManipulationBoundaryFeedback 事件,讓視窗在遇到邊緣時稍微移動。

您可以在任何操作事件 (Cancel 事件除外) 的事件引數上呼叫 ManipulationBoundaryFeedback 方法來取消操作。 當您呼叫 Cancel 時,不再會引發操作事件,且會針對觸控發生滑鼠事件。 下表描述操作取消時間與所發生的滑鼠事件之間的關聯性。

其中呼叫 Cancel 的事件 針對已發生之輸入所發生的滑鼠事件
ManipulationStartingManipulationStarted 滑鼠向下事件。
ManipulationDelta 滑鼠向下和滑鼠移動事件。
ManipulationInertiaStartingManipulationCompleted 滑鼠向下、滑鼠移動和滑鼠向上事件。

請注意,如果您在操作處於慣性時呼叫 Cancel,此方法會傳回 false,而且輸入不會引發滑鼠事件。

觸控與操作事件之間的關聯性

UIElement 一律可以接收觸控事件。 當 IsManipulationEnabled 屬性設定為 true 時,UIElement 可以同時接收觸控和操作事件。 如果未處理 TouchDown 事件 (也就是 Handled 屬性是 false),則操作邏輯會擷取對元素的觸控並產生操作事件。 如果 Handled 屬性在 true 事件中設定為 TouchDown,則操作邏輯不會產生操作事件。 下圖示範觸控事件與操作事件之間的關聯性。

觸控與操作事件之間的關聯性 觸控和操作事件

下列清單描述上圖中所顯示之觸控事件與操作事件間的關聯性。

焦點

WPF 中有兩個關於焦點的主要概念︰鍵盤焦點和邏輯焦點。

鍵盤焦點

鍵盤焦點是指接收鍵盤輸入的項目。 整個桌面只能有一個項目有鍵盤焦點。 在 WPF 中,具有鍵盤焦點的元素將 IsKeyboardFocused 設定為 true。 靜態 Keyboard 方法 FocusedElement 會傳回目前具有鍵盤焦點的元素。

按 Tab 鍵跳到元素,或以滑鼠按一下特定元素 (例如 TextBox),即可取得鍵盤焦點。 您也可以在 Focus 類別上使用 Keyboard 方法,以程式設計方式取得鍵盤焦點。 Focus 會嘗試提供指定的元素鍵盤焦點。 Focus 傳回的元素是目前具有鍵盤焦點的元素。

為了讓元素取得鍵盤焦點,Focusable 屬性和 IsVisible 屬性必須設定為 true。 某些類別,例如 Panel,預設已將 Focusable 設定為 false;因此,如果您想要讓該元素能夠取得焦點,您可能必須將此屬性設定為 true

下列範例使用 Focus 來將鍵盤焦點設定在 Button。 在應用程式中設定初始焦點的建議位置是在 Loaded 事件處理常式中。

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

如需鍵盤焦點的詳細資訊,請參閱焦點概觀

邏輯焦點

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

焦點範圍是會在其範圍內持續追蹤 FocusedElement 的容器元素。 當焦點離開焦點範圍時,焦點項目就會失去鍵盤焦點,但卻仍然保有邏輯焦點。 當焦點回到焦點範圍時,焦點項目就會取得鍵盤焦點。 這讓鍵盤焦點能在多個焦點範圍間變更,但確保焦點範圍內的焦點項目仍是焦點返回時的焦點項目。

可以藉由將 FocusManager 附加屬性 IsFocusScope 設定為 true,或使用 SetIsFocusScope 方法來設定附加屬性,以將元素轉換成可延伸應用程式標記語言 (XAML) 的焦點範圍。

下列範例會藉由設定 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)

WPF 中預設為焦點範圍的類別是 WindowMenuToolBarContextMenu

具有鍵盤焦點的元素也會有其所屬焦點範圍的邏輯焦點;因此,使用 Focus 類別上的 Keyboard 方法或基礎元素類別,將焦點設定為元素,將會嘗試提供元素鍵盤焦點和邏輯焦點。

若要判斷焦點範圍中的焦點元素,請使用 GetFocusedElement。 若要變更焦點範圍的焦點元素,請使用 SetFocusedElement

如需邏輯焦點的詳細資訊,請參閱焦點概觀

滑鼠位置

WPF 輸入 API 會提供有關座標空間的實用資訊。 例如,座標 (0,0) 是左上角的座標,但樹狀結構中的項目左上方為何? 為輸入目標的項目? 附加事件處理常式的項目? 或其他事項? 為了避免混淆,WPF 輸入 API 會需要您在處理透過滑鼠取得的座標時指定參考框架。 GetPosition 方法會傳回滑鼠指標相對於指定元素的座標。

滑鼠捕捉

滑鼠裝置專門保留稱為滑鼠捕捉的強制回應特性。 滑鼠捕捉是用來維護啟動拖放作業後的轉換輸入狀態;因此,不一定會發生涉及滑鼠指標之額定螢幕位置的其他作業。 拖曳期間,使用者按一下就會中止拖放,這樣會在拖曳原點持有滑鼠捕捉時,讓大部分的 mouseover 提示為不適當。 輸入系統會公開可判斷滑鼠擷取狀態的 API,以及可強制滑鼠擷取到特定元素或清除滑鼠擷取狀態的 API。 如需拖放作業的詳細資訊,請參閱拖放概觀

命令

命令比裝置輸入更接近語意層級的輸入處理。 命令是簡單指示詞,例如 CutCopyPasteOpen。 命令適用於將命令邏輯集中。 相同的命令可以從 Menu、在 ToolBar 上或透過鍵盤快速鍵存取。 命令也提供一種機制,可在命令變成無法使用時停用控制項。

RoutedCommandICommand 的 WPF 實作。 執行 RoutedCommand 時,命令目標上會引發 PreviewExecutedExecuted 事件,通道和泡泡會通過元素樹狀結構,就像是其他輸入。 如果未設定命令目標,則具有鍵盤焦點的項目就是命令目標。 執行命令的邏輯會附加至 CommandBinding。 當 Executed 事件到達該特定命令的 CommandBinding 時,會呼叫 ExecutedRoutedEventHandler 上的 CommandBinding。 此處理常式會執行命令的動作。

如需命令的詳細資訊,請參閱命令概觀

WPF 提供由 ApplicationCommandsMediaCommandsComponentCommandsNavigationCommandsEditingCommands 組成的通用命令程式庫,或者您可以定義自己的命令。

下列範例示範如何設定 MenuItem,以便其在按一下時叫用 Paste 上的 TextBox 命令時,假設 TextBox 具有鍵盤焦點。

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

如需 WPF 中命令的詳細資訊,請參閱命令概觀

輸入系統和基底項目

MouseKeyboardStylus 類別所定義的附加事件這類的輸入事件是由輸入系統引發,且會根據在執行階段點閱測試視覺化樹狀結構,來插入物件模型中的特定位置。

MouseKeyboardStylus 定義為附加事件的每個事件,也會由基礎元素類別 UIElementContentElement 重新公開為新的路由事件。 可處理原始附加事件並重複使用事件資料的類別會產生基底項目路由事件。

輸入事件透過其基底項目輸入事件實作與特定來源項目建立關聯時,可以透過事件路由的其餘部分進行路由傳送,而其乃根據邏輯和視覺化樹狀結構物件的組合,並透過應用程式碼進行處理。 一般而言,在 UIElementContentElement 上使用路由事件來處理這些裝置相關輸入事件會比較簡單,因為您可以使用 XAML 和程式碼的更直覺式事件處理常式語法。 您可以選擇改為處理已初始程序的附加事件,但會遇到幾個問題︰基底項目類別處理可能會將附加事件標記為已處理,而且您需要使用存取子方法,而不是真正事件語法,才能附加所附加事件的處理常式。

後續步驟

您現在有數種技巧可處理 WPF 中的輸入。 您也應該進一步了解各種類型的輸入事件以及 WPF 所使用的路由事件機制。

提供詳細說明 WPF 架構元素和事件路由的其他資源。 如需詳細資訊,請參閱下列概觀:命令概觀焦點概觀基底項目概觀WPF 中的樹狀結構路由事件概觀

另請參閱