author | Description | title | label | template | keywords | ms.author | ms.date | ms.topic | ms.prod | ms.technology | pm-contact | design-contact | doc-status | ms.localizationpriority |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Karl-Bridge-Microsoft |
Learn how accelerator keys can improve the usability and accessibility of UWP apps. |
Keyboard accelerators |
Keyboard accelerators |
detail.hbs |
keyboard, accelerator, accelerator key, keyboard shortcuts, accessibility, navigation, focus, text, input, user interactions, gamepad, remote |
kbridge |
10/10/2017 |
article |
windows |
uwp |
chigy |
miguelrb |
Draft |
medium |
Accelerator keys (or keyboard accelerators) are keyboard shortcuts that improve the usability and accessibility of your Windows applications by providing an intuitive way for users to invoke common actions or commands without navigating the app UI.
See the Access keys topic for details on navigating the UI of a Windows application with keyboard shortcuts.
Note
A keyboard is indispensable for users with certain disabilities (see Keyboard accessibility), and is also an important tool for users who prefer it as a more efficient way to interact with an app.
Accelerators typically include the function keys F1 through F12 or some combination of a standard key paired with one or more modifier keys (CTRL, Shift).
Note
The UWP platform controls have built-in keyboard accelerators. For example, ListView supports Ctrl+A for selecting all the items in the list, and RichEditBox supports Ctrl+Tab for inserting a Tab in the text box. These built-in keyboard accelerators are referred to as control accelerators and are executed only if the focus is on the element or one of its children. Accelerators defined by you using the keyboard accelerator APIs discussed here are referred to as app accelerators.
Keyboard accelerators are not available for every action but are often associated with commands exposed in menus (and should be specified with the menu item content). Accelerators can also be associated with actions that do not have equivalent menu items. However, because users rely on an application's menus to discover and learn the available command set, you should try to make discovery of accelerators as easy as possible (using labels or established patterns can help with this).
Keyboard accelerators described in a menu item label
We recommend that you specify keyboard accelerators wherever appropriate in your UI, and support accelerators in all custom controls.
-
Keyboard accelerators make your app more accessible for users with motor disabilities, including those users who can press only one key at a time or have difficulty using a mouse.**
A well-designed keyboard UI is an important aspect of software accessibility. It enables users with vision impairments or who have certain motor disabilities to navigate an app and interact with its features. Such users might not be able to operate a mouse and instead rely on various assistive technologies such as keyboard enhancement tools, on-screen keyboards, screen enlargers, screen readers, and voice input utilities. For these users, comprehensive command coverage is crucial.
-
Keyboard accelerators make your app more usable for power users who prefer to interact through the keyboard.
Experienced users often have a strong preference for using the keyboard because keyboard-based commands can be entered more quickly and don't require them to remove their hands from the keyboard. For these users, efficiency and consistency are crucial; comprehensiveness is important only for the most frequently used commands.
Use the KeyboardAccelerator APIs to create keyboard accelerators in UWP apps. With these APIs, you don't have to handle multiple KeyDown events to detect the key combination pressed, and you can localize accelerators in the app resources.
We recommend that you set keyboard accelerators for the most common actions in your app and document them using the menu item label or tooltip. In this example, we declare keyboard accelerators only for the Rename and Copy commands.
<CommandBar Margin="0,200" AccessKey="M">
<AppBarButton
Icon="Share"
Label="Share"
Click="OnShare"
AccessKey="S" />
<AppBarButton
Icon="Copy"
Label="Copy"
ToolTipService.ToolTip="Copy (Ctrl+C)"
Click="OnCopy"
AccessKey="C">
<AppBarButton.KeyboardAccelerators>
<KeyboardAccelerator
Modifiers="Control"
Key="C" />
</AppBarButton.KeyboardAccelerators>
</AppBarButton>
<AppBarButton
Icon="Delete"
Label="Delete"
Click="OnDelete"
AccessKey="D" />
<AppBarSeparator/>
<AppBarButton
Icon="Rename"
Label="Rename"
ToolTipService.ToolTip="Rename (F2)"
Click="OnRename"
AccessKey="R">
<AppBarButton.KeyboardAccelerators>
<KeyboardAccelerator
Modifiers="None" Key="F2" />
</AppBarButton.KeyboardAccelerators>
</AppBarButton>
<AppBarButton
Icon="SelectAll"
Label="Select"
Click="OnSelect"
AccessKey="A" />
<CommandBar.SecondaryCommands>
<AppBarButton
Icon="OpenWith"
Label="Sources"
AccessKey="S">
<AppBarButton.Flyout>
<MenuFlyout>
<ToggleMenuFlyoutItem Text="OneDrive" />
<ToggleMenuFlyoutItem Text="Contacts" />
<ToggleMenuFlyoutItem Text="Photos"/>
<ToggleMenuFlyoutItem Text="Videos"/>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarToggleButton
Icon="Save"
Label="Auto Save"
IsChecked="True"
AccessKey="A"/>
</CommandBar.SecondaryCommands>
</CommandBar>
Keyboard accelerator described in a tooltip
The UIElement object has a KeyboardAccelerator collection, KeyboardAccelerators, where you specify your custom KeyboardAccelerator objects and define the keystrokes for the keyboard accelerator:
-
Key - the VirtualKey used for the keyboard accelerator.
-
Modifiers – the VirtualKeyModifiers used for the keyboard accelerator. If Modifiers is not set, the default value is None.
Note
Single key (A, Delete, F2, Spacebar, Esc, Multimedia Key) accelerators and multi-key accelerators (Ctrl+Shift+M) are supported. However, Gamepad virtual keys are not supported.
Some accelerators work only in specific scopes while others work app-wide.
For example, Microsoft Outlook includes the following accelerators:
- Ctrl+B, Ctrl+I and ESC work only on the scope of the send email form
- Ctrl+1 and Ctrl+2 work app-wide
Context menu actions affect only specific areas or elements, such as the selected characters in a text editor or a song in a playlist. For this reason, we recommend setting the scope of keyboard accelerators for context menu items to the parent of the context menu.
Use the ScopeOwner property to specify the scope of the keyboard accelerator. This code demonstrates how to implement a context menu on a ListView with scoped keyboard accelerators:
<ListView x:Name="MyList">
<ListView.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Share" Icon="Share"/>
<MenuFlyoutItem Text="Copy" Icon="Copy">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator
Modifiers="Control"
Key="C"
ScopeOwner="{x:Bind MyList }" />
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Delete" Icon="Delete" />
<MenuFlyoutSeparator />
<MenuFlyoutItem Text="Rename">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator
Modifiers="None"
Key="F2"
ScopeOwner="{x:Bind MyList}" />
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Select" />
</MenuFlyout>
</ListView.ContextFlyout>
<ListViewItem>Track 1</ListViewItem>
<ListViewItem>Alternative Track 1</ListViewItem>
</ListView>
The ScopeOwner attribute of the MenuFlyoutItem.KeyboardAccelerators element marks the accelerator as scoped instead of global (the default is null, or global). For more detail, see the Resolving accelerators section later in this topic.
The KeyboardAccelerator object uses the UI Automation (UIA) control pattern to take action when an accelerator is invoked.
The UIA [control patterns] expose common control functionality. For example, the Button control implements the Invoke control pattern to support the Click event (typically a control is invoked by clicking, double-clicking, or pressing Enter, a predefined keyboard shortcut, or some alternate combination of keystrokes). When a keyboard accelerator is used to invoke a control, the XAML framework looks up whether the control implements the Invoke control pattern and, if so, activates it (it is not necessary to listen for the KeyboardAcceleratorInvoked event).
In the following example, Control+S triggers the Click event because the button implements the Invoke pattern.
<Button Content="Save" Click="OnSave">
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="S" Modifiers="Control" />
</Button.KeyboardAccelerators>
</Button>
If an element implements multiple control patterns, only one can be activated through an accelerator. The control patterns are prioritized as follows:
- Invoke (Button)
- Toggle (Checkbox)
- Selection (ListView)
- Expand/Collapse (ComboBox)
If no match is identified, the accelerator is invalid and a debug message is provided ("No automation patterns for this component found. Implement all desired behavior in the Invoked event. Setting Handled to true in your event handler suppresses this message.")
The Invoked event of the KeyboardAccelerator object is fired when the accelerator is executed. The KeyboardAcceleratorInvokedEventArgs event object includes the following properties:
- Handled (Boolean): Setting this to true prevents the event triggering the control pattern and stops accelerator event bubbling. The default is false.
- Element (DependencyObject): The object that contains the accelerator.
Here we demonstrate how to define a collection of keyboard accelerators and how to handle the Invoked event.
<ListView x:Name="MyListView">
<ListView.KeyboardAccelerators>
<KeyboardAccelerator Key="A" Modifiers="Control,Shift" Invoked="SelectAllInvoked" />
<KeyboardAccelerator Key="F5" Invoked="RefreshInvoked" />
</ListView.KeyboardAccelerators>
</ListView>
void SelectAllInvoked (KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
CustomSelectAll(MyListView);
args.Handled = true;
}
void RefreshInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
Refresh(MyListView);
args.Handled = true;
}
If a control is disabled, the associated accelerator is also disabled. In the following example, because the IsEnabled property of the ListView is set to false, the associated Control+A accelerator can't be invoked.
<ListView >
<ListView.KeyboardAccelerators>
<KeyboardAccelerator Key="A"
Modifiers="Control"
Invoked="CustomListViewSelecAllInvoked" />
</ListView.KeyboardAccelerators>
<TextBox>
<TextBox.KeyboardAccelerators>
<KeyboardAccelerator
Key="A"
Modifiers="Control"
Invoked="CustomTextSelecAllInvoked"
IsEnabled="False" />
</TextBox.KeyboardAccelerators>
</TextBox>
<ListView>
Parent and child controls can share the same accelerator. In this case, the parent control can be invoked even if the child has focus and its accelerator is disabled.
Screen readers such as Narrator can announce the keyboard accelerator key combination to users. By default, this is each modifier (in the VirtualModifiers enum order) followed by the key (and separated by "+" signs). You can customize this through the AcceleratorKey AutomationProperties attached property. If more than one accelerator is specified, only the first is announced.
In this example, the AutomationProperty.AcceleratorKey returns the string "Control+Shift+A":
<ListView x:Name="MyListView">
<ListView.KeyboardAccelerators>
<KeyboardAccelerator
Key="A"
Modifiers="Control,Shift"
Invoked="CustomSelectAllInvoked" />
<KeyboardAccelerator
Key="F5"
Modifiers="None"
Invoked="RefreshInvoked" />
</ListView.KeyboardAccelerators>
</ListView>
Note
Setting AutomationProperties.AcceleratorKey doesn't enable keyboard functionality, it only indicates to the UIA framework which keys are used.
We recommend that you make keyboard accelerators consistent across UWP applications. Users have to memorize keyboard accelerators and expect the same (or similar) results.
This might not always be possible due to differences in functionality across apps.
Editing | Common Keyboard Accelerator |
---|---|
Begin editing mode | Ctrl + E |
Select all items in a focused control or window | Ctrl + A |
Search and replace | Ctrl + H |
Undo | Ctrl + Z |
Redo | Ctrl + Y |
Delete selection and copy it to the clipboard | Ctrl + X |
Copy selection to the clipboard | Ctrl + C, Ctrl + Insert |
Paste the contents of the clipboard | Ctrl + V, Shift + Insert |
Paste the contents of the clipboard (with options) | Ctrl + Alt + V |
Rename an item | F2 |
Add a new item | Ctrl + N |
Add a new secondary item | Ctrl + Shift + N |
Delete selected item (with undo) | Del, Ctrl+D |
Delete selected item (without undo) | Shift + Del |
Bold | Ctrl + B |
Underline | Ctrl + U |
Italic | Ctrl + I |
Navigation | |
---|---|
Find content in a focused control or Window | Ctrl + F |
Go to the next search result | F3 |
Other Actions | |
---|---|
Add favorites | Ctrl + D |
Refresh | F5 or Ctrl + R |
Zoom In | Ctrl + + |
Zoom out | Ctrl + - |
Zoom to default view | Ctrl + 0 |
Save | Ctrl + S |
Close | Ctrl + W |
Ctrl + P |
Notice that some of the combinations are not valid for localized versions of Windows. For example, in the Spanish version of Windows, Ctrl+N is used for bold instead of Ctrl+B. We recommend providing localized keyboard accelerators if the app is localized.
As keyboard accelerators are not typically described directly in the UI of your UWP application, you can improve discoverability through tooltips, which display automatically when the user moves focus to, presses and holds, or hovers the mouse pointer over a control. The tooltip can identify whether a control has an associated keyboard accelerator and, if so, what the accelerator key combination is.
Starting with Windows 10, version 1803, when KeyboardAccelerators are declared, controls present the corresponding key combinations in a tooltip by default (unless they are associated with MenuFlyoutItem and ToggleMenuFlyoutItem objects, see the Labels). If a control has more than one accelerator defined, only the first is presented in the tooltip.
Accelerator key combo in tooltip
For AppBarButton and AppBarToggleButton objects, the keyboard accelerator is appended to the label.
<AppBarButton Label="Save" Icon="Save">
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="S" Modifiers="Control" />
</Button.KeyboardAccelerators>
</AppBarButton>
Accelerator key combo appended to control label
Control the presentation behavior by using the KeyboardAcceleratorPlacementMode property, which accepts two values: Auto or Hidden.
<Button Content="Save" Click="OnSave">
<Button.KeyboardAccelerators>
<KeyboardAccelerator
Key="S" Modifiers="Control"
KeyboardAcceleratorPlacementMode="Hidden" />
</Button.KeyboardAccelerators>
</Button>
In some cases, you might need to present a tooltip relative to another element (typically a container object). For example, a Pivot control that displays the tooltip for a PivotItem with the Pivot header.
Here, we show how to use the KeyboardAcceleratorPlacementTarget property to display the keyboard accelerator key combination for a Save button with the Grid container instead of the button.
<Grid x:Name="Container" Padding="30">
<Button Content="Save" Click="OnSave">
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="S" Modifiers="Control"
KeyboardAcceleratorPlacementTarget="{x:Bind Container}"/>
</Button.KeyboardAccelerators>
</Button>
</Grid>
In some cases, we recommend using a control's label to identify whether the control has an associated keyboard accelerator and, if so, what the accelerator key combination is.
Some platform controls do this by default, specifically the MenuFlyoutItem and ToggleMenuFlyoutItem objects, while the AppBarButton and the AppBarToggleButton do it when they appear in the overflow menu of the CommandBar.
Keyboard accelerators described in a menu item label
You can override the default accelerator text for the label through the KeyboardAcceleratorTextOverride property of the MenuFlyoutItem, ToggleMenuFlyoutItem, AppBarButton, and AppBarToggleButton controls (use a single space for no text).
Note
The override text is not be presented if the system cannot detect an attached keyboard (you can check this yourself through the KeyboardPresent property).
Here, we review some low-level aspects of keyboard accelerators.
Accelerators are composed of two types of keys: modifiers and non-modifiers. Modifier keys include Shift, Menu, Control, and the Windows key, which are exposed through VirtualKeyModifiers. Non-modifiers are any virtual key, such as Delete, F3, Spacebar, Esc, and all alphanumeric and punctuation keys. A keyboard accelerator is invoked when the user presses a non-modifier key while they press and hold one or more modifier keys. For example, if the user presses Ctrl+Shift+M, when the user presses M, the framework checks the modifiers (Ctrl and Shift) and fires the accelerator, if it exists.
Note
By design, the accelerator autorepeats (for example, when the user presses Ctrl+Shift and then holds down M, the accelerator is invoked repeatedly until M is released). This behavior cannot be modified.
Input events occur in a specific sequence that you can intercept and handle based on the requirements of your app.
In XAML, a keystroke is processed as if there is just one input bubbling pipeline. This input pipeline is used by the KeyDown/KeyUp events and character input. For example, if an element has focus and the user presses a key down, a KeyDown event is raised on the element, followed by the parent of the element, and so on up the tree, until the args.Handled property is true.
The KeyDown event is also used by some controls to implement the built-in control accelerators. When a control has a keyboard accelerator, it handles the KeyDown event, which means that there won't be KeyDown event bubbling. For example, the RichEditBox supports copy with Ctrl+C. When Ctrl is pressed, the KeyDown event is fired and bubbles, but when the user presses C at the same time, the KeyDown event is marked Handled and is not raised (unless the handledEventsToo parameter of UIElement.AddHandler is set to true).
As the CharacterReceived event is fired after the KeyDown event for text controls such as TextBox, you can cancel character input in the KeyDown event handler.
The preview input events are fired before any other events. If you don't handle these events, the accelerator for the element that has the focus is fired, followed by the KeyDown event. Both events bubble until handled.
Order of events:
Preview KeyDown events … App accelerator OnKeyDown method KeyDown event App accelerators on the parent OnKeyDown method on the parent KeyDown event on the parent (Bubbles to the root) … CharacterReceived event PreviewKeyUp events KeyUpEvents
When the accelerator event is handled, the KeyDown event is also marked as handled. The KeyUp event remains unhandled.
A keyboard accelerator event bubbles from the element that has focus up to the root. If the event isn't handled, the XAML framework looks for other unscoped app accelerators outside of the bubbling path.
When two keyboard accelerators are defined with the same key combination, the first keyboard accelerator found on the visual tree is invoked.
Scoped keyboard accelerators are invoked only when focus is inside a specific scope. For example, in a Grid that contains dozens of controls, a keyboard accelerator can be invoked for a control only when focus is within the Grid (the scope owner).
The UIElement.TryInvokeKeyboardAccelerator method invokes any matching accelerators in the subtree of the element.
The UIElement.OnProcessKeyboardAccelerators method is executed before the keyboard accelerator. This method passes a ProcessKeyboardAcceleratorArgs object that contains the key, the modifier, and a Boolean indicating whether the keyboard accelerator is handled. If marked as handled, the keyboard accelerator bubbles (so the outside keyboard accelerator is never invoked).
Note
OnProcessKeyboardAccelerators always fires, whether handled or not (similar to the OnKeyDown event). You must check whether the event was marked as handled.
In this example, we use OnProcessKeyboardAccelerators and TryInvokeKeyboardAccelerator to scope keyboard accelerators to the Page object:
protected override void OnProcessKeyboardAccelerators(
ProcessKeyboardAcceleratorArgs args)
{
if(args.Handled != true)
{
this.TryInvokeKeyboardAccelerator(args);
args.Handled = true;
}
}
We recommend localizing all keyboard accelerators. You can do this with the standard UWP resources (.resw) file and the x:Uid attribute in your XAML declarations. In this example, the Windows Runtime automatically loads the resources.
Keyboard accelerator localization with UWP resources file
<Button x:Uid="myButton" Click="OnSave">
<Button.KeyboardAccelerators>
<KeyboardAccelerator x:Uid="myKeyAccelerator" Modifiers="Control"/>
</Button.KeyAccelerators>
</Button>
Here is an example of programmatically defining an accelerator:
void AddAccelerator(
VirtualKeyModifiers keyModifiers,
VirtualKey key,
TypedEventHandler<KeyboardAccelerator, KeyboardAcceleratorInvokedEventArgs> handler )
{
var accelerator =
new KeyboardAccelerator()
{
Modifiers = keyModifiers, Key = key
};
accelerator.Invoked = handler;
this.KeyAccelerators.Add(accelerator);
}
Note
KeyboardAccelerator is not shareable, the same KeyboardAccelerator can't be added to multiple elements.
You can handle the KeyboardAccelerator.Invoked event to override the default KeyboardAccelerator behavior.
This example shows how to override the "Select all" command (Ctrl+A keyboard accelerator) in a custom ListView control. We also set the Handled property to true to stop the event bubbling further.
public class MyListView : ListView
{
…
protected override void OnKeyboardAcceleratorInvoked(KeyboardAcceleratorInvokedEventArgs args)
{
if(args.Accelerator.Key == VirtualKey.A
&& args.Accelerator.Modifiers == KeyboardModifiers.Control)
{
CustomSelectAll(TypeOfSelection.OnlyNumbers);
args.Handled = true;
}
}
…
}