Providing Keyboard Shortcuts that Work Across the Entire Silverlight Application

Important Information about Techniques

See Understanding Techniques for WCAG Success Criteria for important information about the usage of these informative techniques and how they relate to the normative WCAG 2.1 success criteria. The Applicability section explains the scope of the technique, and the presence of techniques for a specific technology does not imply that the technology can be used in all situations to create content that meets WCAG 2.1.

Applicability

Note

Microsoft has stopped updating and distributing Silverlight, and authors are encouraged to use HTML for accessible web content.

This technique relates to Success Criterion 2.1.1: Keyboard (Sufficient as a way to meet an unwritten technique).

Description

The objective of this technique is to introduce key handling that exists at the application root level of a Silverlight application, rather than per-element key handling. Event handling at the application level as opposed to at the element level is one way to address key equivalence. The key events provide key equivalence for particular user interface elements that a user might otherwise interact with using a mouse. This technique is related to events in the Silverlight programming model, as opposed to in the HTML DOM.

Handling key events at the root level of an application rather than only on the element that was the "source" of a key event is possible because of a Silverlight programming model feature known as event routing. For more information on event routing and how it works, see Events Overview for Silverlight.

This technique demonstrates a "menu" approach to key handling and user interaction. This technique is presented as a companion to , which can be thought of as an "accelerator key/hotkey" approach. The "menu" approach towards keyboard equivalence is perhaps just as common as the "hotkey" approach. It is often simpler to document a menu's key equivalence in a user interface than it is to document key equivalents of particular regions of an application, or to communicate to users that where the current focus is placed is relevant to how keyboard keys are interpreted by the application, even if the key action is relevant to only one of the controls in an interface. If all keys are handled at the top level, the specific focused element is no longer relevant.

In order to originate a key event that Silverlight application code can detect, some element in the Silverlight application must have keyboard focus. One way to assure keyboard focus is to focus the Silverlight plug-in as a whole, as called from within an event handler for Application.Startup. This is shown in the examples.

If an application does handle keys at top level, care should be taken to not interfere with specific text entry control behavior, such as typing into a TextBox. To avoid interactions, the design of key equivalence at the top level of an application typically relies on combinations with key modifiers. The Control/CTRL key is a key that is often used for this purpose. Application authors should also be aware of the implications of browser hosts that might handle the key event at HTML DOM level without making that event available to the Silverlight programming surface. For more information on this concept, see "Keyboard Events and Browser Hosts" section of Keyboard Support Overview for Silverlight on MSDN.

Application authors are responsible for correctly documenting the accelerator keys that are pertinent for their application. There are a variety of techniques for documenting user interface actions that are not described here. One possible suggestion is to include a generalized "Help" button that appears early in the application's reading order, which is focusable and has an AutomationProperties.Name value available as the text content or equivalent. Such a button can be activated without knowing any of the application's accelerator keys, and the activation result could be a new text element that enumerates the possible keys. For example, the application could display a Silverlight Popup with the following content:

Figure 1A screen shot of a sample Popup control that documents specific accelerator keys

Examples

Example 1: Key handling by application root UserControl

This example has only one interactive control for simplicity, but with two possible key combinations for that control being handled as actions. The purpose and explanation of the control is reported through a TextBlock that is associated with the labeled control through use of AutomationProperties.LabeledBy in XAML. The control being illustrated is MultiScaleImage, which supports a zoom-in metaphor for examining an image that redraws at increasingly fine resolutions. For more information on MultiScaleImage, see Deep Zoom on MSDN.

The following is the startup logic at application level that sends focus to Silverlight in the HTML DOM.

       private void Application_Startup(object sender, StartupEventArgs e)
       {
           this.RootVisual = new MainPage();
           //bring overall DOM focus to Silverlight area, so that keys are captured by Silverlight
           System.Windows.Browser.HtmlPage.Plugin.Focus();
       }
       

The following is XAML UI for the main page.

 <UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    x:Class="ApplicationLevelKeyHandling.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" KeyUp="UserControl_KeyUp">

    <StackPanel x:Name="LayoutRoot" Background="White">
        <Button Name="bInstructions" Click="bInstructions_Click">Get Help</Button>
        <Popup Name="p">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <TextBlock FontWeight="Bold">Key</TextBlock>
                <TextBlock FontWeight="Bold" Grid.Column="1">Action</TextBlock>
                <TextBlock Grid.Row="1">Ctrl + Alt + Plus</TextBlock>
                <TextBlock Grid.Row="1" Grid.Column="1">Zooms in on the image</TextBlock>
                <TextBlock Grid.Row="2">Ctrl + Alt + Minus</TextBlock>
                <TextBlock Grid.Row="2" Grid.Column="1">Zooms out of the image</TextBlock>
                <Button Grid.Row="3" Click="button1_Click">Close this Help</Button>
            </Grid>
        </Popup>
        <MultiScaleImage x:Name="deepZoomObject"
         Source="source/dzc_output.xml" 
         MouseLeftButtonDown="DeepZoomObject_MouseLeftButtonDown"
         MouseRightButtonDown="DeepZoomObject_MouseRightButtonDown"
         AutomationProperties.LabeledBy="{Binding ElementName=lblInstructions}"/>
    </StackPanel>
 </UserControl>

The following is the C# logic. Note how the key handlers and mouse handlers reference the same logic function.

        private void UserControl_KeyUp(object sender, KeyEventArgs e)
        {
            if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control &&
                (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt &&
                e.Key == Key.Add)
            {
                DZIn();
            }
            if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control &&
                (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt &&
                e.Key == Key.Subtract)
            {
                DZOut();
            }
        }
        private void DeepZoomObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DZIn();
        }
        private void DeepZoomObject_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            DZOut();
        }
        private void DZIn()
        {
            this.deepZoomObject.ZoomAboutLogicalPoint(3, .5, .5);
        }
        private void DZOut()
        {
            this.deepZoomObject.ZoomAboutLogicalPoint(.333, .5, .5);
        }
        private void bInstructions_Click(object sender, RoutedEventArgs e)
        {

            // Set where the popup will show up on the screen.
            p.VerticalOffset = 25;
            p.HorizontalOffset = 25;
            // Open the popup.
            p.IsOpen = true;
        }
        void button1_Click(object sender, RoutedEventArgs e)
        {
            // Close the popup.
            p.IsOpen = false;

        }

This example is shown in operation in the working example of Application Level Key Handling.

Resources

Resources are for information purposes only, no endorsement implied.

Tests

Procedure

  1. Using a browser that supports Silverlight, open an HTML page that references a Silverlight application through an object tag.
  2. Verify that keyboard focus is somewhere within the Silverlight content area, and not elsewhere in the hosting HTML or hosting browser user interface. If necessary, use TAB key to traverse the overall HTML tab sequence until an interface element within Silverlight displays a visual focus indicator.
  3. Verify that the keys to be used as keyboard equivalent action triggers for the application as a whole are documented for users. For example, text or long text alternative documents key / key combinations and short descriptions of actions.
  4. Verify that pressing the application-specific keys results in the action as expected in the application.
  5. Move keyboard focus throughout other areas of the Silverlight application, and verify that the same keys continue to function application-wide.

Expected Results

#3, #4 and #5 are true.