鍵盤事件

鍵盤事件和焦點

硬體和觸控式鍵盤都有可能發生以下的鍵盤事件。

事件 描述
KeyDown 發生於按下按鍵時。
KeyUp 發生於放開按鍵時。

重要

某些 XAML 控制項會在內部處理輸入事件。 在這種情況下,因為事件接聽程式不會叫用相關處理常式,所以看起來像是沒有發生輸入事件。 這個按鍵子集通常是由類別處理常式處理,為基本鍵盤協助工具提供內建支援。 例如,Button 類別會覆寫空格鍵與 Enter 鍵的 OnKeyDown 事件 (以及 OnPointerPressed),並將這些事件路由傳送到控制項的 Click 事件。 如果按下按鍵是由控制項類別處理,就不會引發 KeyDownKeyUp 事件。
這樣會提供等同叫用按鈕的內建鍵盤,類似使用手指點選或使用滑鼠按一下。 不是空格鍵或 Enter 鍵的按鍵仍會引發 KeyDownKeyUp 事件。 如需以類別為基礎的事件處理如何運作的詳細資訊,請參閱事件與路由事件概觀 (特別是<控制項中的輸入事件處理常式>一節)。

您 UI 中的控制項只有在取得輸入焦點時才會產生鍵盤事件。 個別控制項會在使用者直接按一下或點選配置上的控制項時取得焦點,或是在內容區域內使用 Tab 鍵進入 Tab 順序時取得焦點。

您也可以呼叫控制項的 Focus 方法來強制取得焦點。 這種方式在實作快速鍵時很必要,因為 UI 載入時預設並沒有設定鍵盤焦點。 如需詳細資訊,請參閱這個主題中後面的 快速鍵範例

為了讓控制項接收輸入焦點,必須啟用控制項並顯示它,而且 IsTabStopHitTestVisible 屬性值必須為 true。 這是大多數控制項的預設狀態。 當控制項取得輸入焦點時,它就可以引發和回應鍵盤輸入事件,如這個主題後面所述。 處理 GotFocusLostFocus 事件也可以回應取得或失去焦點的控制項。

根據預設,控制項的 Tab 順序即為它們在 Extensible Application Markup Language (XAML) 中出現的順序。 不過,使用 TabIndex 屬性就可以修改這個順序。 如需詳細資訊,請參閱實作鍵盤協助工具

鍵盤事件處理常式

輸入事件處理常式實作一個提供下列資訊的委派:

  • 事件的傳送者。 發送者會回報附加了事件處理常式的物件。
  • 事件資料。 以鍵盤事件來說,該資料將會是 KeyRoutedEventArgs 的執行個體。 處理常式的委派是 KeyEventHandler。 對大多數處理常式案例來說,最相關的 KeyRoutedEventArgs 屬性是 Key,也有可能是 KeyStatus
  • OriginalSource。 由於鍵盤事件是路由事件,因此事件資料會提供 OriginalSource。 如果您是刻意讓物件透過物件樹反昇,有時 OriginalSource (而不是發送者) 就會成為較為重要的物件。 不過這需要視您的設計而定。 如需如何使用 OriginalSource 而非發送者的相關資訊,請參閱這個主題的<鍵盤路由事件>一節,或參閱 事件與路由事件概觀

附加鍵盤事件處理常式

您可以為任何物件附加鍵盤事件處理函式,只要該事件是該物件的成員即可。 這包含任何 UIElement 衍生類別。 下列 XAML 範例示範如何為 Grid 附加 KeyUp 事件的處理常式。

<Grid KeyUp="Grid_KeyUp">
  ...
</Grid>

您也可以在程式碼中附加事件處理常式。 如需詳細資訊,請參閱事件與路由事件概觀

定義鍵盤事件處理常式

下面的範例示範前面範例中附加的 KeyUp 事件處理常式的不完整事件處理常式定義。

void Grid_KeyUp(object sender, KeyRoutedEventArgs e)
{
    //handling code here
}
Private Sub Grid_KeyUp(ByVal sender As Object, ByVal e As KeyRoutedEventArgs)
    ' handling code here
End Sub
void MyProject::MainPage::Grid_KeyUp(
  Platform::Object^ sender,
  Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
  {
      //handling code here
  }

使用 KeyRoutedEventArgs

所有鍵盤事件都是使用 KeyRoutedEventArgs 代表事件資料,KeyRoutedEventArgs 包含下列屬性:

虛擬機器碼

如果按下按鍵,會引發 KeyDown 事件。 同樣的,如果放開按鍵,會引發 KeyUp 事件。 通常您接聽事件是為了處理特定的按鍵值。 若要判斷按下或放開的是哪一個按鍵,請檢查事件資料中的 Key 值。 Key 會傳回 VirtualKey 值。 VirtualKey 列舉包括所有受支援的按鍵。

輔助按鍵

輔助按鍵是使用者通常會與其他按鍵一起按的按鍵,如 Ctrl 或 Shift。 您的應用程式可以使用這些組合作為自訂鍵盤快速鍵,來叫用應用程式命令。

注意

如需內建的鍵盤快速鍵,請參閱 存取金鑰鍵盤快捷方式。

您可以在「 KeyDown 」和「 KeyUp 」事件處理常式中偵測快速鍵組合。 當非輔助按鍵發生鍵盤事件時,您可以檢查輔助按鍵是否處於已按下的狀態。

或者,您也可以使用 CoreWindow (的 GetKeyState ()函式(透過 CoreWindow 取得) ()) 也可以在按下非輔助按鍵時,用來檢查修飾詞的狀態。

下列範例會執行第二個方法,同時也包含第一個執行的存根程式碼。

注意

Alt 鍵以 VirtualKey.Menu 值表示。

快速鍵範例

下列範例示範如何執行一組自訂快速鍵。 在這個範例中,使用者可以使用「播放」、「暫停」以及「停止」按鈕或 Ctrl+P、Ctrl+A 以及 Ctrl+S 鍵盤快速鍵來控制媒體的播放。 按鈕 XAML 使用工具提示以及按鈕標籤中的 AutomationProperties 屬性顯示捷徑。 這個自我說明文件對於提高應用程式的可用性及協助性是很重要的。 如需詳細資訊,請參閱鍵盤協助工具

另請注意,頁面載入時會將輸入焦點設給本身。 如果沒有這個步驟,就沒有控制項會取得初始輸入焦點,而且除非使用者手動設定輸入焦點 (例如,按 Tab 鍵或按一下控制項),否則應用程式將無法引發輸入事件。

<Grid KeyDown="Grid_KeyDown">

  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>

  <MediaElement x:Name="DemoMovie" Source="xbox.wmv"
    Width="500" Height="500" Margin="20" HorizontalAlignment="Center" />

  <StackPanel Grid.Row="1" Margin="10"
    Orientation="Horizontal" HorizontalAlignment="Center">

    <Button x:Name="PlayButton" Click="MediaButton_Click"
      ToolTipService.ToolTip="Shortcut key: Ctrl+P"
      AutomationProperties.AcceleratorKey="Control P">
      <TextBlock>Play</TextBlock>
    </Button>

    <Button x:Name="PauseButton" Click="MediaButton_Click"
      ToolTipService.ToolTip="Shortcut key: Ctrl+A"
      AutomationProperties.AcceleratorKey="Control A">
      <TextBlock>Pause</TextBlock>
    </Button>

    <Button x:Name="StopButton" Click="MediaButton_Click"
      ToolTipService.ToolTip="Shortcut key: Ctrl+S"
      AutomationProperties.AcceleratorKey="Control S">
      <TextBlock>Stop</TextBlock>
    </Button>

  </StackPanel>

</Grid>
//showing implementations but not header definitions
void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
{
    (void) e;    // Unused parameter
    this->Loaded+=ref new RoutedEventHandler(this,&amp;MainPage::ProgrammaticFocus);
}
void MainPage::ProgrammaticFocus(Object^ sender, RoutedEventArgs^ e) 
{
    this->Focus(Windows::UI::Xaml::FocusState::Programmatic);
}

void KeyboardSupport::MainPage::MediaButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    FrameworkElement^ fe = safe_cast<FrameworkElement^>(sender);
    if (fe->Name == "PlayButton") {DemoMovie->Play();}
    if (fe->Name == "PauseButton") {DemoMovie->Pause();}
    if (fe->Name == "StopButton") {DemoMovie->Stop();}
}


bool KeyboardSupport::MainPage::IsCtrlKeyPressed()
{
    auto ctrlState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::Control);
    return (ctrlState & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down;
}

void KeyboardSupport::MainPage::Grid_KeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
{
    if (e->Key == VirtualKey::Control) isCtrlKeyPressed = true;
}


void KeyboardSupport::MainPage::Grid_KeyUp(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
{
    if (IsCtrlKeyPressed()) 
    {
        if (e->Key==VirtualKey::P) { DemoMovie->Play(); }
        if (e->Key==VirtualKey::A) { DemoMovie->Pause(); }
        if (e->Key==VirtualKey::S) { DemoMovie->Stop(); }
    }
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Set the input focus to ensure that keyboard events are raised.
    this.Loaded += delegate { this.Focus(FocusState.Programmatic); };
}

private void MediaButton_Click(object sender, RoutedEventArgs e)
{
    switch ((sender as Button).Name)
    {
        case "PlayButton": DemoMovie.Play(); break;
        case "PauseButton": DemoMovie.Pause(); break;
        case "StopButton": DemoMovie.Stop(); break;
    }
}

private static bool IsCtrlKeyPressed()
{
    var ctrlState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control);
    return (ctrlState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down;
}

private void Grid_KeyDown(object sender, KeyRoutedEventArgs e)
{
    if (IsCtrlKeyPressed())
    {
        switch (e.Key)
        {
            case VirtualKey.P: DemoMovie.Play(); break;
            case VirtualKey.A: DemoMovie.Pause(); break;
            case VirtualKey.S: DemoMovie.Stop(); break;
        }
    }
}
Private isCtrlKeyPressed As Boolean
Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)

End Sub

Private Function IsCtrlKeyPressed As Boolean
    Dim ctrlState As CoreVirtualKeyStates = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control);
    Return (ctrlState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down;
End Function

Private Sub Grid_KeyDown(sender As Object, e As KeyRoutedEventArgs)
    If IsCtrlKeyPressed() Then
        Select Case e.Key
            Case Windows.System.VirtualKey.P
                DemoMovie.Play()
            Case Windows.System.VirtualKey.A
                DemoMovie.Pause()
            Case Windows.System.VirtualKey.S
                DemoMovie.Stop()
        End Select
    End If
End Sub

Private Sub MediaButton_Click(sender As Object, e As RoutedEventArgs)
    Dim fe As FrameworkElement = CType(sender, FrameworkElement)
    Select Case fe.Name
        Case "PlayButton"
            DemoMovie.Play()
        Case "PauseButton"
            DemoMovie.Pause()
        Case "StopButton"
            DemoMovie.Stop()
    End Select
End Sub

注意

在 XAML 中設定 AutomationProperties.AcceleratorKeyAutomationProperties.AccessKey 可提供字串資訊,其中記載用於叫用該特定動作的快速鍵。 Microsoft UI 自動化用戶端 (例如朗讀程式) 會擷取此資訊,通常直接提供給使用者。

設定 AutomationProperties.AcceleratorKeyAutomationProperties.AccessKey 本身不會有任何動作。 您還是需要附加 KeyDownKeyUp 事件的處理常式,才能實際在應用程式中實作鍵盤快速鍵行為。 另外,也不會自動提供便捷鍵的底線文字裝飾。 如果您希望在 UI 中顯示有底線的文字,必須以內嵌 Underline 格式明確地為助憶鍵中的特定鍵加上文字底線。

鍵盤路由事件

有一些特定事件是路由事件,包括 KeyDownKeyUp。 路由事件使用事件反昇路由策略。 反昇路由策略表示事件源自於子物件,並接著反昇路由到物件樹中相繼的父物件。 這是處理相同事件並與相同事件資料互動的機會。

以下列 XAML 範例為例,它會處理一個 Canvas 和兩個 Button 物件的 KeyUp 事件。 在這個情況下,如果在任一 Button 物件具有焦點時放開按鍵,則會引發 KeyUp 事件。 接著,事件會反昇到父 Canvas

<StackPanel KeyUp="StackPanel_KeyUp">
  <Button Name="ButtonA" Content="Button A"/>
  <Button Name="ButtonB" Content="Button B"/>
  <TextBlock Name="statusTextBlock"/>
</StackPanel>

下面的範例示範如何實作前一個範例中對應的 XAML 內容的 KeyUp 事件處理常式。

void StackPanel_KeyUp(object sender, KeyRoutedEventArgs e)
{
    statusTextBlock.Text = String.Format(
        "The key {0} was pressed while focus was on {1}",
        e.Key.ToString(), (e.OriginalSource as FrameworkElement).Name);
}

請注意,前面處理常式中有用到 OriginalSource 屬性。 這裡的 OriginalSource 會回報引發事件的物件。 物件不可以是 StackPanel,因為 StackPanel 不是控制項而且不可以取得焦點。 在 StackPanel 內的兩個按鈕中,只有一個可能引發事件,但是是哪一個呢? 如果您是在處理父物件上的事件,可以使用 OriginalSource 來辨別實際的事件來源物件。

事件資料中的 Handled 屬性

視您的事件處理策略而定,您可能會只想使用一個事件處理常式來應對反昇事件。 例如,如果您有附加到其中一個 Button 控制項的特定 KeyUp 處理常式,該處理常式會先使用它來處理該事件。 在這個情況下,您可能不想讓父面板也同時處理事件。 這時,您可以在事件資料中使用 Handled 屬性。

使用路由事件資料類別中的 Handled 屬性是為了報告先前在事件路由上登錄的另一個處理常式已經執行。 這會影響路由事件系統的行為。 將事件處理常式中的 Handled 設成 true 時,該事件會停止路由而不會傳送給相繼的父元素。

AddHandler 和已處理的鍵盤事件

您可以使用一項特別的技術,以附加可以在已標示為處理過的事件上作用的處理常式。 這項技術會使用 AddHandler 方法來註冊處理常式,而不是使用 XAML 屬性或特定語言的語法來新增處理常式,例如 C 中的 + = # 。

這項技術的一般限制在於 AddHandler API 是採用一個 RoutedEvent 類型的參數來識別相關的路由事件。 並非所有路由事件都提供 RoutedEvent 識別項,因此這項考量也就影響到哪些路由事件仍然可以在 Handled 案例中處理。 KeyDownKeyUp 事件在 UIElement 上已有路由事件識別項 (KeyDownEventKeyUpEvent)。 不過,其他事件 (例如 TextBox.TextChanged) 並沒有路由事件識別項,因此也就不能與 AddHandler 技術搭配使用。

覆寫鍵盤事件和行為

您可以覆寫特定控制項 (例如 GridView) 的按鍵事件,為各種輸入裝置 (包括鍵盤與遊戲台) 提供一致的焦點瀏覽。

在下列範例中,我們會將控制項子類別化並覆寫 KeyDown 行為,以便在按下任何箭號鍵時,將焦點移至 GridView 內容。

  public class CustomGridView : GridView
  {
    protected override void OnKeyDown(KeyRoutedEventArgs e)
    {
      // Override arrow key behaviors.
      if (e.Key != Windows.System.VirtualKey.Left && e.Key !=
        Windows.System.VirtualKey.Right && e.Key !=
          Windows.System.VirtualKey.Down && e.Key !=
            Windows.System.VirtualKey.Up)
              base.OnKeyDown(e);
      else
        FocusManager.TryMoveFocus(FocusNavigationDirection.Down);
    }
  }

注意

如果使用 GridView 只是為了版面配置,請考慮使用其他控制項,例如 ItemsControlItemsWrapGrid

命令

少數 UI 元素提供命令功能的內建支援。 命令功能在相關實作中使用輸入相關路由事件。 它會叫用單一命令處理常式來處理相關的 UI 輸入,例如特定指標動作或特定快速鍵。

如果某個 UI 元素擁有命令功能,請考慮使用它的命令 API,而非任何特定輸入事件。 如需詳細資訊,請參閱 ButtonBase.Command

您也可實作 ICommand 來封裝從一般事件處理常式叫用的命令功能。 透過這種方式,即使沒有可用的 Command 屬性,您還是可以使用命令功能。

文字輸入和控制項

特定控制項會以它們自己的處理方式應對鍵盤事件。 例如,TextBox 這個控制項的設計是擷取使用鍵盤輸入的文字,然後以視覺化方式呈現文字。 它在自己的邏輯中使用 KeyUpKeyDown 來擷取按鍵輸入動作,然後如果文字真的變更了,就會引發它自己的 TextChanged 事件。

一般來說,您仍然可以將 KeyUpKeyDown 的處理常式加到 TextBox,或加到任何要用來處理文字輸入的相關控制項。 不過,基於控制項本身的設計目的,控制項可能不會對透過按鍵事件導向它的所有按鍵值都提供回應。 每個控制項都有它的特定行為。

例如,ButtonBase (Button 的基礎類別) 會處理 KeyUp,這樣它就可以檢查空格鍵或 Enter 鍵。 ButtonBaseKeyUp 視為等同滑鼠左鍵,可以引發 Click 事件。 事件的處理會在 ButtonBase 覆寫虛擬方法 OnKeyUp 時完成。 在實作中,它會將 Handled 設定成 true。 以空格鍵為例,結果是接聽按鍵事件的任何父按鈕,都不會收到自己的處理常式的已處理事件。

另一個範例是 TextBox。 某些按鍵(例如方向鍵)不會依 文字方塊 被視為文字,而是被視為特定的控制項 UI 行為。 TextBox 會將這些事件案例標示為已處理。

自訂控制項可以藉由覆寫 OnKeyDownOnKeyUp 來為關鍵事件執行自己的類似覆寫行為 / ****。 如果您的自訂控制項會處理特定的快速鍵,或具有類似於針對 TextBox 所述之情況的控制項或焦點行為,就應該將這個邏輯放入您自己的 OnKeyDown / OnKeyUp 覆寫中。

觸控式鍵盤

文字輸入控制項會自動支援觸控式鍵盤。 當使用者使用觸控輸入將輸入焦點設為文字控制項時,觸控式鍵盤會自動出現。 當輸入焦點不在文字控制項時,觸控式鍵盤就會隱藏起來。

當觸控式鍵盤出現時,它會自動定位您的 UI,以確保取得焦點的元素保持永遠可見。 這可能會造成 UI 中其他重要的區域跑到螢幕外。 不過,您可以停用預設行為,並在觸控式鍵盤出現時調整 UI。 如需詳細資訊,請參閱觸控式鍵盤範例

如果建立需要文字輸入但不是從標準輸入控制項衍生的自訂控制項,您可以實作正確的 UI 自動化控制項模式來加入對觸控式鍵盤的支援。 如需詳細資訊,請參閱觸控式鍵盤範例

按下觸控式鍵盤上的按鍵,就像按下硬體鍵盤上的按鍵一樣,都會引發 KeyDownKeyUp 事件。 不過,觸控式鍵盤將不會引發 Ctrl+A、Ctrl+Z、Ctrl+X、Ctrl+C 以及 Ctrl+V 的輸入事件,因為它們是保留給輸入控制項的文字操作。

您可以設定文字控制項的輸入範圍,使其符合您預期使用者輸入的資料類型,讓使用者在您的應用程式中輸入資料時更加快速方便。 輸入範圍會提供控制項所預期之文字輸入類型的提示,讓系統可以為該輸入類型提供專用的觸控式鍵盤配置。 例如,如果文字方塊僅用來輸入4位數的 PIN,請將 InputScope 屬性設定為 Number。 這會告訴系統顯示數字鍵台配置,方便使用者輸入 PIN。 如需詳細資訊,請參閱使用輸入範圍來變更觸控式鍵盤

開發人員

設計工具

範例

封存範例