(使用 C#/VB/C++ 和 XAML 的 Windows 应用商店应用)
可以通过使用 C++、C# 或 Visual Basic 为 Windows 构建的 Windows 应用商店应用中的硬件或触摸键盘来响应击键操作。为此,请使用 Windows 8 中提供的键盘事件。
键盘支持对于辅助功能非常重要,并且能够在没有触摸屏时启用该功能。此外,有了键盘支持,对于那些经常使用键盘的用户来说,你的应用更实用。用户应可以使用 Tab 和箭头键导航应用,使用空格键和 Enter 键激活 UI 元素,并使用键盘快捷方式访问命令。
有关文本输入的详细信息,请参阅 显示和编辑文本。
路线图: 本主题与其他主题有何关联?请参阅:
键盘事件
使用 C++、C# 或 Visual Basic 的 Windows 应用商店应用提供以下键盘事件,硬件和触摸键盘都可能会发生这些事件。
事件 | 描述 |
---|---|
KeyDown | 按下某个键时发生。 |
KeyUp | 释放某个键时发生。 |
键盘事件和焦点
仅当 UI 中的控件具有输入焦点时才会生成键盘事件。当用户直接单击或点击布局中的一个控件时该控件获得焦点,或者使用 Tab 键进入内容区域内的 Tab 序列。
也可以调用控件的 Focus 方法来强制使用焦点。当实现快捷键时需要此操作,因为 UI 加载时默认情况下不设置键盘焦点。有关详细信息,请参阅本主题后面部分的快捷键示例。
若要使某个控件接收输入焦点,则必须启用该控件,该控件必须可见并且 IsTabStop 和 HitTestVisible 的属性值必须为 true。这是大多数控件的默认状态。当某个控件具有输入焦点时,该控件可能引发并响应键盘输入事件,如本主题后面部分所述。你也可以通过处理 GotFocus 和 LostFocus 事件来响应接收或丢失焦点的控件。
默认情况下,控件的 Tab 序列顺序为它们在 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 枚举包括使用 C++、C# 或 Visual Basic 的 Windows 应用商店应用支持的所有键。
修改键
修改键是用户通常与其他键组合而按下的键,如 Ctrl 或 Shift。你的应用可以使用这些组合作为键盘快捷方式来调用应用命令。
在使用 C++、C# 或 Visual Basic 的 Windows 应用商店应用中,你可以在 KeyDown 和 KeyUp 事件处理程序中使用代码来检测快捷键组合。然后,你可以跟踪感兴趣的修改键的按下状态。当非修改键发生键盘事件时,可以同时检查修改键是否处于按下状态。
注意 Alt 键由 VirtualKey.Menu 值表示。
快捷键示例
以下示例演示如何实现快捷键。在此示例中,用户可以使用 Play、Pause 和 Stop 按钮或 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,&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();} } 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 (e->Key == VirtualKey::Control) isCtrlKeyPressed = true; else 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 Grid_KeyUp(object sender, KeyRoutedEventArgs e) { if (e.Key == VirtualKey.Control) isCtrlKeyPressed = false; } private void Grid_KeyDown(object sender, KeyRoutedEventArgs e) { if (e.Key == VirtualKey.Control) isCtrlKeyPressed = true; else if (isCtrlKeyPressed) { switch (e.Key) { case VirtualKey.P: DemoMovie.Play(); break; case VirtualKey.A: DemoMovie.Pause(); break; case VirtualKey.S: DemoMovie.Stop(); break; } } } 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 isCtrlKeyPressed As Boolean Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) End Sub Private Sub Grid_KeyUp(sender As Object, e As KeyRoutedEventArgs) If e.Key = Windows.System.VirtualKey.Control Then isCtrlKeyPressed = False End If End Sub Private Sub Grid_KeyDown(sender As Object, e As KeyRoutedEventArgs) If e.Key = Windows.System.VirtualKey.Control Then isCtrlKeyPressed = True 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.AcceleratorKey 或 AutomationProperties.AccessKey 会提供字符串信息,这可记录用于调用该特定操作的快捷键。该信息由 Microsoft UI 自动化客户端(如讲述人)捕获,且通常直接提供给用户。设置 AutomationProperties.AcceleratorKey 或 AutomationProperties.AccessKey 不会自行执行任何操作。你将仍需要附加 KeyDown 或 KeyUp 事件的处理程序,以便在你的应用中真正实现键盘快捷键行为。此外,不会自动为访问键提供带下划线的文本效果。如果你希望在 UI 中显示带下划线的文本,则必须明确对助记键中特定键的文本标注下划线,作为嵌入式 Underline 格式。
键盘路由事件
有些事件为路由事件,这些事件包括 KeyDown 和 KeyUp。路由事件使用浮升路由策略。浮升路由策略意味着某个事件从子对象开始,然后向上路由到对象树中的连续父对象。这样便提供了处理相同事件以及与相同数据数据交互的另一个机会。
请考虑以下 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 属性
根据你的事件处理策略,你可能希望只有一个事件处理程序对浮升事件进行响应。例如,如果已将特定的 KeyUp 处理程序附加到其中一个 Button 控件,则这是处理该事件的第一次机会。这种情况下,你可能不希望父面板也处理该事件。对于此方案,可以使用事件数据中的 Handled 属性。
路由事件数据类中的 Handled 属性用于报告之前在事件路由上注册的另一个处理程序已进行操作。这会影响路由事件系统的行为。在事件处理程序中将 Handled 设置为 true 时,该事件停止路由,但不会发送到连续的父元素。
AddHandler 和 already-handled 键盘事件
可以使用特殊技术来附加处理程序,该技术对已标记为已处理的事件进行操作。此技术使用 AddHandler 方法来注册处理程序,而不是使用 XAML 属性或特定语言的添加处理程序语法,如在 C# 中的“+=”。此技术的局限性通常是 AddHandler API 带有一个类型为 RoutedEvent 的参数,该参数标识有问题的路由事件。并非所有路由事件都提供 RoutedEvent 标识符,因此此注意事项会影响在 Handled 情况下仍然可以处理哪些路由事件。 KeyDown 和 KeyUp 事件在 UIElement 上拥有路由事件标识符( KeyDownEvent 和 KeyUpEvent)。但是,其他事件(如 TextBox.TextChanged)没有路由事件标识符,因此不能使用 AddHandler 技术。
命令处理
少量 UI 元素提供对命令的内置支持。命令在其基础实现中使用与输入相关的路由事件。它能够通过调用一个命令处理程序来处理相关的 UI 输入,如某个指针操作或特定加速器键。
如果命令处理可用于 UI 元素,可以考虑使用它的命令处理 API 代替任何具体的输入事件。有关详细信息,请参阅 ButtonBase.Command。
还可以实现 ICommand 来封装从普通事件处理程序中调用的命令功能。这样即使没有可用的 Command 属性,也能够使用命令。
文本输入和控件
某些控件通过自身的处理来对键盘事件作出响应。例如, TextBox 是一个控件,设计用于捕获然后在视觉上显示使用键盘输入的文本。它以自己的逻辑使用 KeyUp 和 KeyDown 来捕获击键,然后,即使在文本实际上已更改的情况下,也还是会引发自己的 TextChanged 事件。
通常,仍然可以将 KeyUp 和 KeyDown 的处理程序添加到 TextBox 或计划处理文本输入的任何相关控件中。但是,作为其预期设计,一个控件可能并不会对通过键事件指向它的所有键值作出响应。行为特定于每个控件。
例如, ButtonBase( Button 的基类)处理 KeyUp 以便可以检查空格键或 Enter 键。ButtonBase 认为 KeyUp 等同于按下鼠标左键以引发 Click 事件。事件的这一处理是在 ButtonBase 覆盖虚拟方法 OnKeyUp 时完成的。其实施过程中会将 Handled 设置为 true。如果空格键未接收到自己处理程序的 already-handled 事件,则结果为某个按钮的任意父按钮侦听键事件。
另一个示例是 TextBox。TextBox 不会将某些键(如箭头键)视为文本,而是视为特定于控件 UI 的行为。TextBox 将这些事件案例标记为已处理。
自定义控件可以通过重写 OnKeyDown/ OnKeyUp 来为键事件实现自己的相似重写行为。如果你的自定义控件处理特定加速器键或者具有类似于为 TextBox 描述的方案的控件或焦点行为,则应该将该逻辑放置在自己的 OnKeyDown/OnKeyUp 重写中。
触摸键盘
文本输入控件提供对触摸键盘的自动支持。当用户将输入焦点设置为使用触摸输入的文本控件时,触摸键盘会自动出现。当输入焦点不在文本控件上时,隐藏触摸键盘。
当出现触摸键盘时,会自动重新定位你的 UI 以确保可以看见具有焦点的元素。这可能会导致 UI 的其他重要区域移出屏幕。但是,你可以禁用默认行为并在出现触摸键盘时对自己的 UI 进行调整。有关详细信息,请参阅 响应出现屏幕键盘的示例。
如果创建需要文本输入,但并非从标准文本输入控件派生的自定义控件,则可以通过实现正确的 UI 自动化控件模式来添加触摸键盘支持。有关详细信息,请参阅 触摸键盘示例。
按下触摸键盘上的键引发 KeyDown 和 KeyUp 事件,就像在硬件键盘上按下键一样。但是,触摸键盘不会引发 Ctrl+A、Ctrl+Z、Ctrl+X、Ctrl+C 和 Ctrl+V 的输入事件,这些是输入控件中保留的文本操作。