Accesibilidad de teclado

Si tu aplicación no proporciona un buen acceso de teclado, los usuarios invidentes o con problemas de motricidad pueden llegar a tener dificultades para usar tu aplicación o, probablemente, no puedan usarla.

Navegación de teclado entre elementos de la interfaz de usuario

Para usar el teclado con un control, el control debe tener el foco y, para recibirlo (sin usar un puntero), debe ser accesible en un diseño de interfaz de usuario a través de navegación mediante tabulación. De manera predeterminada, el orden de tabulación de los controles es el mismo que el orden en el que se agregaron a una superficie de diseño, enumerada en XAML, o que se agregó mediante programación a un contenedor.

En la mayoría de los casos, el orden predeterminado basado en cómo definiste los controles en XAML es el mejor orden, especialmente porque es el orden en el que los lectores de pantalla leen los controles. No obstante, el orden predeterminado no necesariamente corresponde al orden visual. La posición de visualización real probablemente dependa del contenedor de diseño primario y ciertas propiedades que puedes establecer en los elementos secundarios para influir en el diseño. Para asegurarte de que la aplicación tiene un buen orden de tabulación, prueba este comportamiento tú mismo. Especialmente si tienes una metáfora de cuadrícula o de tabla para el diseño, el orden de la tabla podría ser distinto al orden en que leerían los usuarios. Esto no siempre es un problema. Pero asegúrate de probar la funcionalidad de tu aplicación como una interfaz de usuario táctil y como una interfaz de usuario con teclado, y comprueba que la interfaz de usuario tiene sentido con ambos métodos.

Puedes hacer que el orden de tabulación coincida con el orden visual ajustando el código XAML. O bien, puedes invalidar el orden de tabulación predeterminado configurando la propiedad TabIndex, como se muestra en el siguiente ejemplo de un diseño Grid que usa la navegación mediante tabulación por columnas primero.

XAML

<!--Custom tab order.-->
<Grid>
  <Grid.RowDefinitions>...</Grid.RowDefinitions>
  <Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>

  <TextBlock Grid.Column="1" HorizontalAlignment="Center">Groom</TextBlock>
  <TextBlock Grid.Column="2" HorizontalAlignment="Center">Bride</TextBlock>

  <TextBlock Grid.Row="1">First name</TextBlock>
  <TextBox x:Name="GroomFirstName" Grid.Row="1" Grid.Column="1" TabIndex="1"/>
  <TextBox x:Name="BrideFirstName" Grid.Row="1" Grid.Column="2" TabIndex="3"/>

  <TextBlock Grid.Row="2">Last name</TextBlock>
  <TextBox x:Name="GroomLastName" Grid.Row="2" Grid.Column="1" TabIndex="2"/>
  <TextBox x:Name="BrideLastName" Grid.Row="2" Grid.Column="2" TabIndex="4"/>
</Grid>

Es posible que quieras excluir un control del orden de tabulación. Por lo general, esto se logra convirtiendo el control a no interactivo, por ejemplo, configurando la propiedad IsEnabled en false Un control deshabilitado se excluye automáticamente del orden de tabulación. Pero, en ocasiones, quizá quieras excluir un control del orden de tabulación incluso si no está deshabilitado. En este caso, puedes establecer la propiedad IsTabStop false.

Todos los elementos que pueden tener foco suelen estar en el orden de tabulación de manera predeterminada. La excepción es que es posible que ciertos tipos de presentación de texto como RichTextBlock tengan foco con el fin de que el portapapeles pueda acceder a ellos para seleccionar texto; sin embargo, no se encuentran en el orden de tabulación porque no se espera que los elementos de texto estático estén en el orden de tabulación. No suelen ser interactivos (pueden invocarse y no requieren entrada de texto, pero no admiten el patrón de control de texto que admite la búsqueda y el ajuste de puntos de selección en el texto). El texto no debe dar a entender que, si se coloca el foco en él, pueda existir la posibilidad de realizar alguna actividad. Las tecnologías de asistencia seguirán detectando los elementos de texto y los lectores de pantalla los leerán en voz alta, pero esto dependerá de otro tipo de técnicas que no tienen que ver con encontrar estos elementos en el orden de tabulación práctico.

Tanto si ajustas los valores TabIndex como si usas el orden predeterminado, se aplicarán las siguientes reglas:

  • Si TabIndex no está establecido en un elemento, el valor predeterminado usado es Int32.MaxValue y el orden de tabulación se basa en el orden de declaración en las colecciones XAML o secundarias.
  • Si TabIndex se establece en un elemento:
    • Los elementos de la interfaz de usuario con TabIndex igual a 0 se añaden al orden de tabulación según el orden de declaración en XAML o colecciones secundarias.
    • Los elementos de la interfaz de usuario con una propiedad TabIndex mayor que 0 se añaden al orden de tabulación según el valor TabIndex.
    • Los elementos de la interfaz de usuario con una propiedad TabIndex menor que 0 se añaden al orden de tabulación y aparecen antes de cualquier valor cero. Esto difiere potencialmente de la administración del atributo tabindex de HTML (y el tabindex negativo no se admitía en especificaciones HTML más antiguas).

Por ejemplo, el fragmento de código siguiente muestra una colección de elementos con una configuración tabIndex variable (B se asigna el valor de Int32.MaxValue o 2.147.483.647).

<StackPanel Background="#333">
  <StackPanel Background="#FF33FF">
    <Button>A</Button>
    <Button TabIndex="2147483647">B</Button>
    <Button>C</Button>
  </StackPanel>
  <StackPanel Background="#33FFFF">
    <Button TabIndex="1">D</Button>
    <Button TabIndex="1">E</Button>
    <Button TabIndex="0">F</Button>
  </StackPanel>
</StackPanel>

Esto da como resultado el siguiente orden de tabulación:

  1. F
  2. D
  3. E
  4. Un
  5. B
  6. C

Navegación por teclado dentro de un elemento de interfaz de usuario

Para los elementos compuestos, es importante asegurar una correcta navegación interna entre los elementos contenidos. Un elemento compuesto puede administrar su elemento secundario activo actual para reducir la sobrecarga que implica hacer que todos los elementos secundarios tengan foco. Un elemento compuesto de este tipo se incluye en el orden de tabulación y controla los eventos de navegación por teclado. Muchos de los controles compuestos ya tienen una lógica de navegación interna integrada para controlar la administración de eventos. Por ejemplo, el recorrido mediante teclas de dirección de los elementos está habilitado de manera predeterminada en los controles ListView, GridView, ListBox y FlipView .

Alternativas de teclado para eventos y acciones de puntero para elementos de control específicos

Asegúrate de que los elementos de la interfaz de usuario en los que pueda hacerse clic también puedan invocarse con el teclado. Para usar el teclado con un elemento de la interfaz de usuario, el elemento debe tener foco. Solo las clases que derivan de Control admiten navegación mediante tabulación y foco.

Para aquellos elementos de la interfaz de usuario que puedan invocarse, implementa controladores de eventos de teclado para las tecla ENTRAR y la barra espaciadora. Esto hace que la compatibilidad de accesibilidad de teclado básica sea completa y permite a los usuarios lograr escenarios de aplicación básicos mediante el uso del teclado solamente, es decir, los usuarios pueden alcanzar todos los elementos de la interfaz de usuario interactivos y activar la funcionalidad predeterminada.

En los casos en los que un elemento que quieres usar en la interfaz de usuario no pueda tener foco, podrías crear tu propio control personalizado. Debes establecer la propiedad IsTabStop en true para habilitar el foco y debes proporcionar una indicación visual del estado enfocado creando un estado visual que decore la interfaz de usuario con un indicador de foco. Sin embargo, suele ser más fácil usar la composición de controles para que el soporte de los modelos de automatización de la interfaz de usuario de Microsoft y automatización del mismo nivel, foco y detenciones de tabulación sea administrado por el control dentro del cual elijas que se componga tu contenido.

Por ejemplo, en lugar de administrar un evento de presión de puntero en una clase Image, podrías encapsularlo en una clase Button para obtener compatibilidad para foco, teclado y puntero.

XAML

<!--Don't do this.-->
<Image Source="sample.jpg" PointerPressed="Image_PointerPressed"/>

<!--Do this instead.-->
<Button Click="Button_Click"><Image Source="sample.jpg"/></Button>

Métodos abreviados de teclado

Además de implementar la navegación por teclado y la activación para tu aplicación, una práctica recomendada es implementar métodos abreviados para la funcionalidad de la aplicación. La navegación mediante tabulación proporciona un buen nivel básico de compatibilidad con teclado, pero con formas complejas, probablemente también quieras agregar compatibilidad para teclas de método abreviado. Esto puede hacer que el uso de tu aplicación sea más eficiente, incluso para aquellos que usan tanto dispositivos de puntero como un teclado.

Un método abreviado es una combinación de teclas que mejora la productividad al proporcionar al usuario una forma eficaz de acceder a las funciones de la aplicación. Existen dos tipos de métodos abreviados:

  • Una tecla de acceso es un método abreviado a un elemento de la interfaz de usuario de la aplicación. Las teclas de acceso consisten en la tecla Alt y una tecla de letra.
  • Una tecla de aceleración es un método abreviado a un comando de aplicación. Tu aplicación podría tener una interfaz de usuario que corresponda exactamente al comando. Las teclas de aceleración consisten en la tecla Ctrl y una tecla de letra.

Es fundamental que proporciones a los usuarios que dependen de los lectores de pantalla y otra tecnología de asistencia un método sencillo para descubrir las teclas de los métodos abreviados de la aplicación. Comunica las teclas de método abreviado mediante la información sobre herramientas, los nombres accesibles, las descripciones accesibles o alguna otra forma de comunicación en pantalla. Como mínimo, las teclas de método abreviado deben estar correctamente documentadas en el contenido de la ayuda de la aplicación.

Puedes documentar las teclas de acceso mediante lectores de pantalla configurando la propiedad adjunta AutomationProperties.AccessKey como una cadena que describe la tecla de método abreviado. También existe una propiedad adjunta AutomationProperties.AcceleratorKey para documentar las teclas de método abreviado no mnemotécnicas, aunque los lectores de pantalla generalmente tratan ambas propiedades de la misma manera. Intenta documentar las teclas de método abreviado de diferentes maneras, mediante documentación de ayuda escrita, propiedades de automatización e información sobre herramientas.

En el siguiente ejemplo se muestra cómo documentar las teclas de método abreviado para botones de detención, pausa y reproducción de medios.

XAML

<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>

Importante

Si se establece AutomationProperties.AcceleratorKey o AutomationProperties.AccessKey , no se habilita la funcionalidad de teclado. Únicamente notifica al marco de trabajo de automatización de la interfaz de usuario qué teclas se deben usar para poder pasar esta información a los usuarios mediante las tecnologías de asistencia. La implementación de la administración de teclas debe hacerse en el código, no en XAML. Deberás adjuntar controladores para los eventos KeyDown o KeyUp en el control correspondiente para implementar realmente el comportamiento de los métodos abreviados de teclado en tu aplicación. Además, el detalle de texto subrayado en una tecla de acceso no se proporciona de manera automática. Si quieres mostrar texto subrayado en la interfaz de usuario, debes subrayar explícitamente el texto de la tecla de acceso específica como formato Underline en línea.

Por cuestiones de simplicidad, el ejemplo anterior omite el uso de recursos para cadenas como Ctrl+A. No obstante, también debes tener en cuenta las teclas de método abreviado durante la localización. La localización de las teclas de método abreviado es relevante porque la elección de la tecla que se usa como método abreviado suele depender de la etiqueta de texto visible del elemento.

Para obtener más información sobre la implementación de las teclas de método abreviado, consulta Teclas de método abreviado en las Directrices para la interacción de la experiencia de usuarios de Windows.

Implementar un controlador de eventos de tecla

Los eventos de entrada como los eventos de tecla usan un concepto de evento denominado eventos enrutados. Un evento enrutado se puede propagar por los elementos secundarios de un control compuesto, lo que permite a un control primario controlar los eventos de varios elementos secundarios. Este modelo de evento es conveniente para definir acciones de teclas de método abreviado en un control que contenga varias partes compuestas que no estén diseñadas para admitir foco ni para formar parte del orden de tabulación.

Para obtener código de ejemplo que muestra cómo escribir un controlador de eventos de clave que incluye la comprobación de modificadores como la tecla Ctrl, consulte Interacciones del teclado.

Navegación mediante teclado para controles personalizados

Se recomienda el uso de teclas de dirección como métodos abreviados de teclado para navegar entre elementos secundarios, en los casos en que dichos elementos mantengan una relación espacial entre sí. Si los nodos de la vista de árbol tienen sub-elementos secundarios separados para administrar la activación de nodos y las acciones de expandir/contraer, usa las teclas de flecha izquierda y derecha para proporcionar la funcionalidad de expandir/contraer del teclado. Si tienes un control orientado que admite el recorrido direccional dentro del contenido del control, usa las teclas de dirección adecuadas.

Por lo general, para implementar un control de teclas personalizado para controles personalizados se incluye una invalidación de los métodos OnKeyDown y OnKeyUp como parte de la lógica de la clase.

Un ejemplo de un estado visual para un indicador de foco

Ya hemos mencionado que los controles personalizados que el usuario habilite para que tengan el foco deben tener un indicador de foco visual. Normalmente, ese indicador de foco es tan simple como dibujar un rectángulo justo alrededor del rectángulo de límite normal del control. La clase Rectangle del foco visual es un elemento del mismo nivel que el resto de la composición del control en una plantilla de control, pero se establece inicialmente con un valor Visibility en Collapsed porque el control aún no tiene el foco. Después, cuando el control obtiene el foco, se invoca un estado visual que establece específicamente el valor de Visibility en Visible. Una vez que el foco se mueve en otro lugar, se llama a otro estado visual y la visibilidad se contrae.

Todos los controles XAML predeterminados mostrarán un indicador de foco visual adecuado cuando reciban el foco (si es que pueden recibirlo). También hay distintos aspectos potenciales según el tema seleccionado por el usuario (en especial si el usuario usa un modo de contraste alto). Si usas los controles XAML en la interfaz de usuario y no reemplazas las plantillas de control, no tienes que hacer nada más para obtener indicadores de foco visual en los controles que se comportan y se muestran correctamente. Si lo que intentas es volver a crear la plantilla de un control, o si tienes curiosidad sobre cómo los controles XAML proporcionan sus indicadores de foco visual, en el resto de esta sección se explica cómo se logra en XAML y la lógica de control.

Este es un ejemplo de XAML que procede de la plantilla XAML predeterminada para un botón.

XAML

<ControlTemplate TargetType="Button">
...
    <Rectangle
      x:Name="FocusVisualWhite"
      IsHitTestVisible="False"
      Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}"
      StrokeEndLineCap="Square"
      StrokeDashArray="1,1"
      Opacity="0"
      StrokeDashOffset="1.5"/>
    <Rectangle
      x:Name="FocusVisualBlack"
      IsHitTestVisible="False"
      Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}"
      StrokeEndLineCap="Square"
      StrokeDashArray="1,1"
      Opacity="0"
      StrokeDashOffset="0.5"/>
...
</ControlTemplate>

De momento, esto es solo la composición. Para controlar la visibilidad del indicador de foco, tienes que definir los estados visuales que alternan la propiedad Visibility. Esto se hace mediante visualStateManager y la propiedad adjunta VisualStateManager.VisualStateGroups, tal como se aplica al elemento raíz que define la composición.

XAML

<ControlTemplate TargetType="Button">
  <Grid>
    <VisualStateManager.VisualStateGroups>
       <!--other visual state groups here-->
       <VisualStateGroup x:Name="FocusStates">
         <VisualState x:Name="Focused">
           <Storyboard>
             <DoubleAnimation
               Storyboard.TargetName="FocusVisualWhite"
               Storyboard.TargetProperty="Opacity"
               To="1" Duration="0"/>
             <DoubleAnimation
               Storyboard.TargetName="FocusVisualBlack"
               Storyboard.TargetProperty="Opacity"
               To="1" Duration="0"/>
         </VisualState>
         <VisualState x:Name="Unfocused" />
         <VisualState x:Name="PointerFocused" />
       </VisualStateGroup>
     <VisualStateManager.VisualStateGroups>
<!--composition is here-->
   </Grid>
</ControlTemplate>

Ten en cuenta que solo uno de los estados con nombre ajusta la propiedad Visibility directamente mientras que los demás están aparentemente vacíos. El funcionamiento de los estados visuales es que en cuanto el control usa otro estado del mismo VisualStateGroup, se cancelan inmediatamente las animaciones aplicadas por el estado anterior. Debido a que el valor predeterminado de Visibility de la composición es Collapsed, el rectángulo no aparecerá. Esto se controla con la lógica de control, mediante la escucha de eventos de foco como GotFocus y la modificación de los estados a GoToState. A menudo esto se realiza automáticamente si usas un control predeterminado o personalizado, basado en un control que ya tenga ese comportamiento.

Accesibilidad de teclado y Windows Phone

Por lo general, un dispositivo de Windows Phone carece de un teclado de hardware dedicado. Sin embargo, un panel de entrada de software (SIP) puede dar cabida a diversos escenarios de accesibilidad de teclado. Los lectores de pantalla pueden leer entradas de texto desde el SIP de Texto e incluso advertir de posibles eliminaciones. Los usuarios podrán saber dónde están sus dedos porque el lector de pantalla es capaz de detectar que el usuario está examinando las teclas, de modo que lee en voz alta el nombre de la tecla examinada. De igual modo, algunos de los conceptos de accesibilidad relativos al teclado se pueden asignar a determinados comportamientos de tecnología de asistencia en los que no se usa el teclado en absoluto. Por ejemplo, incluso si un SIP no incluye una tecla TAB, el Narrador admite un gesto táctil que equivale a presionar dicha tecla, de modo que disponer de un orden de tabulación útil de los controles de una interfaz de usuario sigue siendo un principio de accesibilidad de gran importancia. Las teclas de dirección para navegar por los elementos de controles complejos también se pueden usar como gestos táctiles en el Narrador. Cuando el foco llega a un control que no está destinado a la entrada de texto, Narrador admite un gesto con el que se invoca la acción de dicho control.

Los métodos abreviados de teclado no suelen ser relevantes para las aplicaciones Windows Phone, puesto que un SIP carece de teclas Control o ALT.

Ejemplos

Sugerencia

La aplicación WinUI 3 Gallery incluye ejemplos interactivos de la mayoría de los controles, las características y la funcionalidad de WinUI 3. Obtenga la aplicación en Microsoft Store o el código fuente en GitHub.