Navegación de foco para herramientas de teclado, controlador para juegos, control remoto y accesibilidad

Teclado, remoto y panel D

Usa la navegación de foco para proporcionar experiencias de interacción completas y coherentes en tus aplicaciones de Windows y controles personalizados para usuarios avanzados de teclado, aquellos con discapacidades y otros requisitos de accesibilidad, así como la experiencia de 10 pies de pantallas de televisión y Xbox One.

Información general

La navegación con foco hace referencia al mecanismo subyacente que permite a los usuarios navegar e interactuar con la interfaz de usuario de una aplicación de Windows mediante un teclado, un controlador para juegos o un control remoto.

Nota

Normalmente, los dispositivos de entrada se clasifican como dispositivos que apuntan, como táctil, panel táctil, lápiz y mouse, y dispositivos que no apuntan, como el teclado, el controlador para juegos y el control remoto.

En este tema se describe cómo optimizar una aplicación de Windows y crear experiencias de interacción personalizadas para los usuarios que dependen de tipos de entrada que no apunten.

Aunque nos centramos en la entrada de teclado para controles personalizados en aplicaciones de Windows en equipos, también es importante una experiencia de teclado bien diseñada para teclados de software como el teclado táctil y el Teclado en pantalla (OSK), que admite herramientas de accesibilidad como Narrador de Windows y admiten la experiencia de 10 pies.

Consulte Controlar la entrada del puntero para obtener instrucciones sobre la creación de experiencias personalizadas en aplicaciones windows para dispositivos que apunten.

Para obtener más información general sobre la creación de aplicaciones y experiencias para el teclado, consulte Interacción con el teclado.

Instrucciones generales

Solo los elementos de interfaz de usuario que requieren interacción del usuario deben admitir la navegación de foco, los elementos que no requieren una acción, como imágenes estáticas, no necesitan el foco del teclado. Los lectores de pantalla y las herramientas de accesibilidad similares aún anuncian estos elementos estáticos, incluso cuando no se incluyen en la navegación de foco.

Es importante recordar que, a diferencia de navegar con un dispositivo de puntero, como un mouse o un toque, la navegación de foco es lineal. Al implementar la navegación de foco, tenga en cuenta cómo interactuará un usuario con la aplicación y cuál debe ser la navegación lógica. En la mayoría de los casos, se recomienda que el comportamiento de navegación de foco personalizado siga el patrón de lectura preferido de la referencia cultural del usuario.

Entre otras consideraciones de navegación de foco se incluyen:

  • ¿Los controles se agrupan lógicamente?
  • ¿Hay grupos de controles con mayor importancia?
    • Si es así, ¿esos grupos contienen subgrupos?
  • ¿El diseño requiere navegación direccional personalizada (teclas de dirección) y orden de tabulación?

El libro electrónico Engineering Software for Accessibility tiene un excelente capítulo sobre el diseño de la jerarquía lógica.

Navegación direccional 2D para el teclado

La región de navegación interna 2D de un control, o grupo de controles, se conoce como su "área direccional". Cuando el foco cambia a este objeto, las teclas de dirección del teclado (izquierda, derecha, arriba y abajo) se pueden usar para navegar entre los elementos secundarios dentro del área direccional.

área direccional2D Región de navegación interna, o área direccional, de un grupo de control

Puede usar la propiedad XYFocusKeyboardNavigation (que tiene valores posibles de Auto, Enabled o Disabled) para administrar la navegación interna 2D con las teclas de dirección del teclado.

Nota

El orden de tabulación no se ve afectado por esta propiedad. Para evitar una experiencia de navegación confusa, se recomienda que los elementos secundarios de un área direccional no se especifiquen explícitamente en el orden de navegación de tabulación de la aplicación. Consulte las propiedades UIElement.TabFocusNavigation y TabIndex para obtener más detalles sobre el comportamiento de tabulación de un elemento.

Automático (comportamiento predeterminado)

Cuando se establece en Automático, el comportamiento de navegación direccional viene determinado por la jerarquía de herencia o la herencia del elemento. Si todos los antecesores están en modo predeterminado (establecido en Automático), no se admite la navegación direccional con el teclado.

Deshabilitada

Establezca XYFocusKeyboardNavigation en Deshabilitado para bloquear la navegación direccional al control y sus elementos secundarios.

Comportamiento deshabilitado de XYFocusKeyboardNavigationXYFocusKeyboardNavigation

En este ejemplo, el StackPanel principal (ContainerPrimary) tiene XYFocusKeyboardNavigation establecido en Enabled. Todos los elementos secundarios heredan esta configuración y se pueden navegar a con las teclas de dirección. Sin embargo, los elementos B3 y B4 están en un StackPanel secundario (ContainerSecondary) con XYFocusKeyboardNavigation establecido en Disabled, que invalida el contenedor principal y deshabilita la navegación por la tecla de flecha a sí mismo y entre sus elementos secundarios.

<Grid 
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" 
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="75"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
                Grid.Row="0" 
                FontWeight="ExtraBold" 
                HorizontalTextAlignment="Center"
                TextWrapping="Wrap" 
                Padding="10" />
    <StackPanel Name="ContainerPrimary" 
                XYFocusKeyboardNavigation="Enabled" 
                KeyDown="ContainerPrimary_KeyDown" 
                Orientation="Horizontal" 
                BorderBrush="Green" 
                BorderThickness="2" 
                Grid.Row="1" 
                Padding="10" 
                MaxWidth="200">
        <Button Name="B1" 
                Content="B1" 
                GettingFocus="Btn_GettingFocus" />
        <Button Name="B2" 
                Content="B2" 
                GettingFocus="Btn_GettingFocus" />
        <StackPanel Name="ContainerSecondary" 
                    XYFocusKeyboardNavigation="Disabled" 
                    Orientation="Horizontal" 
                    BorderBrush="Red" 
                    BorderThickness="2">
            <Button Name="B3" 
                    Content="B3" 
                    GettingFocus="Btn_GettingFocus" />
            <Button Name="B4" 
                    Content="B4" 
                    GettingFocus="Btn_GettingFocus" />
        </StackPanel>
    </StackPanel>
</Grid>

Enabled

Establezca XYFocusKeyboardNavigation en Habilitado para admitir la navegación direccional 2D en un control y cada uno de sus objetos secundarios UIElement .

Cuando se establece, la navegación con las teclas de dirección está restringida a elementos dentro del área direccional. La navegación por tabulación no se ve afectada, ya que todos los controles permanecen accesibles a través de su jerarquía de orden de tabulación.

Comportamiento habilitado para XYFocusKeyboardNavigationXYFocusKeyboardNavigation habilitado

En este ejemplo, el StackPanel principal (ContainerPrimary) tiene XYFocusKeyboardNavigation establecido en Enabled. Todos los elementos secundarios heredan esta configuración y se pueden navegar a con las teclas de dirección. Los elementos B3 y B4 se encuentran en un stackPanel secundario (ContainerSecondary) donde XYFocusKeyboardNavigation no está establecido, que luego hereda la configuración del contenedor principal. El elemento B5 no está dentro de un área direccional declarada y no admite la navegación con teclas de flecha, pero sí admite el comportamiento de navegación de tabulación estándar.

<Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="100"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
               Grid.Row="0"
               FontWeight="ExtraBold"
               HorizontalTextAlignment="Center"
               TextWrapping="Wrap"
               Padding="10" />
    <StackPanel Grid.Row="1"
                Orientation="Horizontal"
                HorizontalAlignment="Center">
        <StackPanel Name="ContainerPrimary"
                    XYFocusKeyboardNavigation="Enabled"
                    KeyDown="ContainerPrimary_KeyDown"
                    Orientation="Horizontal"
                    BorderBrush="Green"
                    BorderThickness="2"
                    Padding="5" Margin="5">
            <Button Name="B1"
                    Content="B1"
                    GettingFocus="Btn_GettingFocus" Margin="5" />
            <Button Name="B2"
                    Content="B2"
                    GettingFocus="Btn_GettingFocus" />
            <StackPanel Name="ContainerSecondary"
                        Orientation="Horizontal"
                        BorderBrush="Red"
                        BorderThickness="2"
                        Margin="5">
                <Button Name="B3"
                        Content="B3"
                        GettingFocus="Btn_GettingFocus"
                        Margin="5" />
                <Button Name="B4"
                        Content="B4"
                        GettingFocus="Btn_GettingFocus"
                        Margin="5" />
            </StackPanel>
        </StackPanel>
        <Button Name="B5"
                Content="B5"
                GettingFocus="Btn_GettingFocus"
                Margin="5" />
    </StackPanel>
</Grid>

Puede tener varios niveles de áreas direccionales anidadas. Si todos los elementos primarios tienen XYFocusKeyboardNavigation establecido en Habilitado, se omiten los límites de la región de navegación interna.

Este es un ejemplo de dos áreas direccionales anidadas dentro de un elemento que no admite explícitamente la navegación direccional 2D. En este caso, no se admite la navegación direccional entre las dos áreas anidadas.

XYFocusKeyboardNavigation habilitado y comportamiento anidadoXYFocusKeyboardNavigation habilitado y anidado

Este es un ejemplo más complejo de tres áreas direccionales anidadas en las que:

  • Cuando B1 tiene foco, solo B5 se puede navegar a (y viceversa) porque hay un límite de área direccional en el que XYFocusKeyboardNavigation se establece en Disabled, lo que hace que B2, B3 y B4 no se pueda acceder con las teclas de dirección
  • Cuando B2 tiene el foco, solo se puede navegar A B3 (y viceversa) porque el límite de área direccional impide la navegación de teclas de flecha a B1, B4 y B5.
  • Cuando B4 tiene el foco, se debe usar la tecla Tab para navegar entre los controles.

XYFocusKeyboardNavigation habilitado y comportamiento anidado complejo

XYFocusKeyboardNavigation habilitado y comportamiento anidado complejo

Navegación por pestañas

Aunque las teclas de dirección se pueden usar para la navegación direccional 2D con un control o un grupo de controles, la tecla Tab se puede usar para navegar entre todos los controles de una aplicación de Windows.

Todos los controles interactivos admiten la navegación de teclas Tab de forma predeterminada (la propiedad IsEnabled e IsTabStop son true), con el orden de tabulación lógico derivado del diseño de control en la aplicación. 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.

Evite un orden de tabulación personalizado que haga que el foco salte por la aplicación. Por ejemplo, una lista de controles de un formulario debe tener un orden de tabulación que fluya de arriba a abajo y de izquierda a derecha (dependiendo de la configuración regional).

En esta sección se describe cómo este orden de tabulación se puede personalizar completamente para adaptarse a la aplicación.

Establecimiento del comportamiento de navegación de tabulación

La propiedad TabFocusNavigation de UIElement especifica el comportamiento de navegación de tabulación para todo su árbol de objetos (o área direccional).

Nota

Utilice esta propiedad en lugar de la propiedad Control.TabNavigation para los objetos que no usan un ControlTemplate para definir su apariencia.

Como hemos mencionado en la sección anterior, para evitar una experiencia de navegación confusa, se recomienda que los elementos secundarios de un área direccional no se especifiquen explícitamente en el orden de navegación de tabulación de la aplicación. Consulte las propiedades UIElement.TabFocusNavigation y TabIndex para obtener más detalles sobre el comportamiento de tabulación de un elemento.

En las versiones anteriores a Windows 10 Creators Update (compilación 10.0.15063), la configuración de pestañas se limitaba a los objetos ControlTemplate . Para obtener más información, consulta Control.TabNavigation.

TabFocusNavigation tiene un valor de tipo KeyboardNavigationMode con los siguientes valores posibles (tenga en cuenta que estos ejemplos no son grupos de controles personalizados y no requieren navegación interna con las teclas de dirección):

  • Los índices de tabulación locales (predeterminados) se reconocen en el subárbol local dentro del contenedor. En este ejemplo, el orden de tabulación es B1, B2, B3, B4, B5, B6, B7, B1.

    Comportamiento de navegación de pestañas

    Comportamiento de navegación de pestañas "Local"

  • Una vez El contenedor y todos los elementos secundarios reciben el foco una vez. En este ejemplo, el orden de tabulación es B1, B2, B7, B1 (también se muestra la navegación interna con la tecla de flecha).

    Comportamiento de navegación de pestañas

    Comportamiento de navegación de pestañas "Una vez"

  • Ciclo
    El foco vuelve al elemento que se puede centrar inicialmente dentro de un contenedor. En este ejemplo, el orden de tabulación es B1, B2, B3, B4, B5, B6, B2...

    Comportamiento de navegación de pestañas

    Comportamiento de navegación de pestañas "Ciclo"

Este es el código de los ejemplos anteriores (con TabFocusNavigation ="Cycle").

<Grid 
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" 
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="300"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
               Grid.Row="0" 
               FontWeight="ExtraBold" 
               HorizontalTextAlignment="Center"
               TextWrapping="Wrap" 
               Padding="10" />
    <StackPanel Name="ContainerPrimary"
                KeyDown="Container_KeyDown" 
                Orientation="Horizontal" 
                HorizontalAlignment="Center"
                BorderBrush="Green" 
                BorderThickness="2" 
                Grid.Row="1" 
                Padding="10" 
                MaxWidth="200">
        <Button Name="B1" 
                Content="B1" 
                GettingFocus="Btn_GettingFocus" 
                Margin="5"/>
        <StackPanel Name="ContainerSecondary" 
                    KeyDown="Container_KeyDown"
                    XYFocusKeyboardNavigation="Enabled" 
                    TabFocusNavigation ="Cycle"
                    Orientation="Vertical" 
                    VerticalAlignment="Center"
                    BorderBrush="Red" 
                    BorderThickness="2"
                    Padding="5" Margin="5">
            <Button Name="B2" 
                    Content="B2" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B3" 
                    Content="B3" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B4" 
                    Content="B4" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B5" 
                    Content="B5" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
            <Button Name="B6" 
                    Content="B6" 
                    GettingFocus="Btn_GettingFocus" 
                    Margin="5"/>
        </StackPanel>
        <Button Name="B7" 
                Content="B7" 
                GettingFocus="Btn_GettingFocus" 
                Margin="5"/>
    </StackPanel>
</Grid>

TabIndex

Use TabIndex para especificar el orden en el que los elementos reciben el foco cuando el usuario navega por los controles mediante la tecla Tab. Un control con un índice de tabulación inferior recibe el foco antes de un control con un índice superior.

Cuando un control no tiene especificado TabIndex , se le asigna un valor de índice mayor que el valor de índice más alto actual (y la prioridad más baja) de todos los controles interactivos del árbol visual, en función del ámbito.

Todos los elementos secundarios de un control se consideran un ámbito y, si uno de estos elementos también tiene elementos secundarios, se consideran otro ámbito. Cualquier ambigüedad se resuelve eligiendo el primer elemento en el árbol visual del ámbito.

Para excluir un control del orden de tabulación, establezca la propiedad IsTabStop en false.

Invalide el orden de tabulación predeterminado estableciendo la propiedad TabIndex .

Nota

TabIndex funciona de la misma manera con UIElement.TabFocusNavigation y Control.TabNavigation.

Aquí se muestra cómo la navegación de foco puede verse afectada por la propiedad TabIndex en elementos específicos.

Navegación por pestañas

Navegación por pestañas "Local" con el comportamiento de TabIndex

En el ejemplo anterior, hay dos ámbitos:

  • B1, área direccional (B2 - B6) y B7
  • área direccional (B2 - B6)

Cuando B3 (en el área direccional) obtiene el foco, el ámbito cambia y la navegación por tabulación se transfiere al área direccional donde se identifica el mejor candidato para el enfoque posterior. En este caso, B2 seguido de B4, B5 y B6. A continuación, el ámbito cambia de nuevo y el foco se mueve a B1.

Este es el código de este ejemplo.

<Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    TabFocusNavigation="Cycle">
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="300"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock Name="KeyPressed"
               Grid.Row="0"
               FontWeight="ExtraBold"
               HorizontalTextAlignment="Center"
               TextWrapping="Wrap"
               Padding="10" />
    <StackPanel Name="ContainerPrimary"
                KeyDown="Container_KeyDown"
                Orientation="Horizontal"
                HorizontalAlignment="Center"
                BorderBrush="Green"
                BorderThickness="2"
                Grid.Row="1"
                Padding="10"
                MaxWidth="200">
        <Button Name="B1"
                Content="B1"
                TabIndex="1"
                ToolTipService.ToolTip="TabIndex = 1"
                GettingFocus="Btn_GettingFocus"
                Margin="5"/>
        <StackPanel Name="ContainerSecondary"
                    KeyDown="Container_KeyDown"
                    TabFocusNavigation ="Local"
                    Orientation="Vertical"
                    VerticalAlignment="Center"
                    BorderBrush="Red"
                    BorderThickness="2"
                    Padding="5" Margin="5">
            <Button Name="B2"
                    Content="B2"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B3"
                    Content="B3"
                    TabIndex="3"
                    ToolTipService.ToolTip="TabIndex = 3"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B4"
                    Content="B4"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B5"
                    Content="B5"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
            <Button Name="B6"
                    Content="B6"
                    GettingFocus="Btn_GettingFocus"
                    Margin="5"/>
        </StackPanel>
        <Button Name="B7"
                Content="B7"
                TabIndex="2"
                ToolTipService.ToolTip="TabIndex = 2"
                GettingFocus="Btn_GettingFocus"
                Margin="5"/>
    </StackPanel>
</Grid>

Navegación direccional 2D para teclado, controlador para juegos y control remoto

Los tipos de entrada que no son de puntero, como teclado, controlador para juegos, control remoto y herramientas de accesibilidad como Narrador de Windows, comparten un mecanismo común y subyacente para navegar e interactuar con la interfaz de usuario de la aplicación Windows.

En esta sección, tratamos cómo especificar una estrategia de navegación preferida y ajustar la navegación de foco dentro de la aplicación a través de un conjunto de propiedades de estrategia de navegación que admiten todos los tipos de entrada de puntero basados en foco.

Para obtener más información general sobre la creación de aplicaciones y experiencias para Xbox/TV, consulta Interacción del teclado, Diseño para Xbox y TV, y Controlador para juegos y interacciones de control remoto.

Las estrategias de navegación son aplicables al teclado, al controlador para juegos, al control remoto y a varias herramientas de accesibilidad.

Las siguientes propiedades de estrategia de navegación permiten influir en qué control recibe el foco en función de la tecla de dirección, el botón de panel direccional (D-pad) o una presión similar.

  • XYFocusUpNavigationStrategy
  • XYFocusDownNavigationStrategy
  • XYFocusLeftNavigationStrategy
  • XYFocusRightNavigationStrategy

Estas propiedades tienen valores posibles de Auto (valor predeterminado), NavigationDirectionDistance, Projection o RectilinearDistance .

Si se establece en Automático, el comportamiento del elemento se basa en los antecesores del elemento. Si todos los elementos se establecen en Automático, se usa Proyección .

Nota

Otros factores, como el elemento centrado anteriormente o la proximidad al eje de la dirección de navegación, pueden influir en el resultado.

Proyección

La estrategia proyección mueve el foco al primer elemento encontrado cuando el borde del elemento actualmente centrado se proyecta en la dirección de navegación.

En este ejemplo, cada dirección de navegación de foco se establece en Proyección. Observe cómo el foco baja de B1 a B4, omitiendo B3. Esto se debe a que B3 no está en la zona de proyección. Observe también cómo no se identifica un candidato de enfoque cuando se mueve a la izquierda de B1. Esto se debe a que la posición de B2 relativa a B1 elimina B3 como candidato. Si B3 estuviera en la misma fila que B2, sería un candidato viable para la navegación izquierda. B2 es un candidato viable debido a su proximidad sin obstáculos al eje de la dirección de navegación.

Estrategia de navegación de proyección

Estrategia de navegación de proyección

La estrategia NavigationDirectionDistance mueve el foco al elemento más cercano al eje de la dirección de navegación.

El borde del rectángulo delimitador correspondiente a la dirección de navegación se extiende y se proyecta para identificar los destinos candidatos. El primer elemento encontrado se identifica como destino. En el caso de varios candidatos, el elemento más cercano se identifica como destino. Si todavía hay varios candidatos, el elemento situado más arriba o más a la izquierda se identifica como candidato.

NavigationDirectionDistance navigation strategy (Estrategia de navegación de NavigationDirectionDistance)

NavigationDirectionDistance navigation strategy (Estrategia de navegación de NavigationDirectionDistance)

RectilinearDistance

La estrategia RectilinearDistance mueve el foco al elemento más cercano en función de la distancia rectilineal 2D (geometría Taxicab).

La suma de la distancia primaria y la distancia secundaria a cada posible candidato se usa para identificar el mejor cándido. En un empate, se selecciona el primer elemento a la izquierda si la dirección solicitada está hacia arriba o hacia abajo, y se selecciona el primer elemento a la parte superior si la dirección solicitada es izquierda o derecha.

Estrategia de navegación rectilinearDistance

Estrategia de navegación rectilinearDistance

En esta imagen se muestra cómo, cuando B1 tiene el foco y la baja es la dirección solicitada, B3 es el candidato de enfoque RectilinearDistance. Esto se basa en las siguientes cálculos para este ejemplo:

  • Distancia (B1, B3, Abajo) es 10 + 0 = 10
  • Distancia (B1, B2, Abajo) es 0 + 40 = 30
  • Distancia (B1, D, Abajo) es 30 + 0 = 30