Partilhar via


Navegação de foco para teclado, gamepad, controle remoto e ferramentas de acessibilidade

Teclado, controle remoto e D-pad

Use a navegação de foco para fornecer experiências de interação abrangentes e consistentes em seus aplicativos do Windows e controles personalizados para usuários avançados de teclado, pessoas com deficiências e outros requisitos de acessibilidade, bem como a experiência de 10 pés de telas de televisão e do Xbox One.

Visão geral

A navegação de foco refere-se ao mecanismo subjacente que permite que os usuários naveguem e interajam com a interface do usuário de um aplicativo do Windows usando um teclado, gamepad ou controle remoto.

Observação

Os dispositivos de entrada são normalmente classificados como dispositivos apontadores, como toque, touchpad, caneta e mouse, e dispositivos não apontadores, como teclado, gamepad e controle remoto.

Este tópico descreve como otimizar um aplicativo do Windows e criar experiências de interação personalizadas para usuários que dependem de tipos de entrada não apontadores.

Embora nos concentremos na entrada de teclado para controles personalizados em aplicativos do Windows em computadores, uma experiência de teclado bem projetada também é importante para teclados de software, como o teclado virtual e o teclado virtual (OSK), dando suporte a ferramentas de acessibilidade, como o Windows Narrator, e dando suporte à experiência de 10 pés.

Consulte Manipular entrada de ponteiro para obter diretrizes sobre como criar experiências personalizadas em aplicativos Windows para dispositivos apontadores.

Para obter mais informações gerais sobre como criar aplicativos e experiências para teclado, consulte Interação do teclado.

Orientação geral

Somente os elementos da interface do usuário que exigem interação do usuário devem dar suporte à navegação de foco, elementos que não exigem uma ação, como imagens estáticas, não precisam do foco do teclado. Leitores de tela e ferramentas de acessibilidade semelhantes ainda anunciam esses elementos estáticos, mesmo quando eles não estão incluídos na navegação de foco.

É importante lembrar que, ao contrário da navegação com um dispositivo de ponteiro, como um mouse ou toque, a navegação de foco é linear. Ao implementar a navegação de foco, considere como um usuário interagirá com seu aplicativo e qual deve ser a navegação lógica. Na maioria dos casos, recomendamos que o comportamento de navegação de foco personalizado siga o padrão de leitura preferencial da cultura do usuário.

Algumas outras considerações de navegação de foco incluem:

  • Os controles são agrupados logicamente?
  • Existem grupos de controles com maior importância?
    • Em caso afirmativo, esses grupos contêm subgrupos?
  • O layout requer navegação direcional personalizada (teclas de seta) e ordem de tabulação?

O eBook Software de Engenharia para Acessibilidade tem um excelente capítulo sobre como projetar a hierarquia lógica.

Navegação direcional 2D para teclado

A região de navegação interna 2D de um controle, ou grupo de controle, é chamada de "área direcional". Quando o foco muda para esse objeto, as teclas de seta do teclado (esquerda, direita, para cima e para baixo) podem ser usadas para navegar entre os elementos filho dentro da área direcional.

Área direcional2D Região de navegação interna, ou área direcional, de um grupo de controle

Você pode usar a propriedade XYFocusKeyboardNavigation (que tem valores possíveis de Auto, Enabled ou Disabled) para gerenciar a navegação interna 2D com as teclas de seta do teclado.

Observação

A ordem de tabulação não é afetada por essa propriedade. Para evitar uma experiência de navegação confusa, recomendamos que os elementos filho de uma área direcional não sejam especificados explicitamente na ordem de navegação da guia do aplicativo. Consulte as propriedades UIElement.TabFocusNavigation e TabIndex para obter mais detalhes sobre o comportamento de tabulação de um elemento.

Automático (comportamento padrão)

Quando definido como Automático, o comportamento de navegação direcional é determinado pela ancestralidade do elemento ou pela hierarquia de herança. Se todos os ancestrais estiverem no modo padrão (definido como Automático), a navegação direcional com o teclado não será suportada.

Desabilitado

Defina XYFocusKeyboardNavigation como Disabled para bloquear a navegação direcional para o controle e seus elementos filho.

Comportamento desabilitado de XYFocusKeyboardNavigationComportamento desabilitado de XYFocusKeyboardNavigation

Neste exemplo, o StackPanel primário (ContainerPrimary) tem XYFocusKeyboardNavigation definido como Enabled. Todos os elementos filho herdam essa configuração e podem ser navegados com as teclas de seta. No entanto, os elementos B3 e B4 estão em um StackPanel secundário (ContainerSecondary) com XYFocusKeyboardNavigation definido como Disabled, que substitui o contêiner primário e desabilita a navegação da tecla de seta para si mesmo e entre seus elementos filho.

<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

Defina XYFocusKeyboardNavigation como Habilitado para dar suporte à navegação direcional 2D para um controle e cada um de seus objetos filho UIElement.

Quando definido, a navegação com as teclas de seta é restrita a elementos dentro da área direcional. A navegação por guias não é afetada, pois todos os controles permanecem acessíveis por meio de sua hierarquia de ordem de guia.

Comportamento habilitado para XYFocusKeyboardNavigationComportamento habilitado para XYFocusKeyboardNavigation

Neste exemplo, o StackPanel primário (ContainerPrimary) tem XYFocusKeyboardNavigation definido como Enabled. Todos os elementos filho herdam essa configuração e podem ser navegados com as teclas de seta. Os elementos B3 e B4 estão em um StackPanel secundário (ContainerSecondary) em que XYFocusKeyboardNavigation não está definido, que herda a configuração do contêiner primário. O elemento B5 não está dentro de uma área direcional declarada e não dá suporte à navegação por tecla de seta, mas dá suporte ao comportamento de navegação de tabulação padrão.

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

Você pode ter vários níveis de áreas direcionais aninhadas. Se todos os elementos pai tiverem XYFocusKeyboardNavigation definido como Habilitado, os limites da região de navegação interna serão ignorados.

Aqui está um exemplo de duas áreas direcionais aninhadas em um elemento que não dá suporte explícito à navegação direcional 2D. Nesse caso, a navegação direcional não é suportada entre as duas áreas aninhadas.

XYFocusKeyboardNavigation habilitado e comportamento aninhadoXYFocusKeyboardNavigation habilitado e comportamento aninhado

Aqui está um exemplo mais complexo de três áreas direcionais aninhadas em que:

  • Quando B1 tem foco, somente B5 pode ser navegado (e vice-versa) porque há um limite de área direcional em que XYFocusKeyboardNavigation definido como Desativado, tornando B2, B3 e B4 inacessíveis com as teclas de seta
  • Quando B2 tem foco, somente B3 pode ser navegado (e vice-versa) porque o limite da área direcional impede a navegação das teclas de seta para B1, B4 e B5
  • Quando B4 tem foco, a tecla Tab deve ser usada para navegar entre os controles

XYFocusKeyboardNavigation habilitado e comportamento aninhado complexo

XYFocusKeyboardNavigation habilitado e comportamento aninhado complexo

Navegação por tabulação

Embora as teclas de seta possam ser usadas para navegação direcional 2D com um controle ou grupo de controle, a tecla Tab pode ser usada para navegar entre todos os controles em um aplicativo do Windows.

Todos os controles interativos dão suporte à navegação por tecla Tab por padrão (as propriedades IsEnabled e IsTabStop são verdadeiras), com a ordem de tabulação lógica derivada do layout de controle em seu aplicativo. No entanto, a ordem padrão não corresponde necessariamente à ordem visual. A posição de exibição real pode depender do contêiner de layout pai e de certas propriedades que você pode definir nos elementos filho para influenciar o layout.

Evite uma ordem de tabulação personalizada que faça o foco salte aleatoriamente em seu aplicativo. Por exemplo, uma lista de controles em um formulário deve ter uma ordem de tabulação que se mova de cima para baixo e da esquerda para a direita (dependendo da localidade).

Nesta seção, descrevemos como essa ordem de tabulação pode ser totalmente personalizada para se adequar ao seu aplicativo.

Definir o comportamento de navegação da guia

A propriedade TabFocusNavigation de UIElement especifica o comportamento de navegação da guia para toda a árvore de objetos (ou área direcional).

Observação

Use essa propriedade em vez da propriedade Control.TabNavigation para objetos que não usam um ControlTemplate para definir sua aparência.

Como mencionamos na seção anterior, para evitar uma experiência de navegação confusa, recomendamos que os elementos filho de uma área direcional não sejam especificados explicitamente na ordem de navegação da guia do seu aplicativo. Consulte as propriedades UIElement.TabFocusNavigation e TabIndex para obter mais detalhes sobre o comportamento de tabulação de um elemento.

Para versões anteriores ao Windows 10 Atualização para Criadores (build 10.0.15063), as configurações da guia foram limitadas a objetos ControlTemplate . Para obter mais informações, consulte Control.TabNavigation.

TabFocusNavigation tem um valor do tipo KeyboardNavigationMode com os seguintes valores possíveis (observe que esses exemplos não são grupos de controle personalizados e não exigem navegação interna com as teclas de direção):

  • Os índices de guia locais (padrão) são reconhecidos na subárvore local dentro do contêiner. Para este exemplo, a ordem de tabulação é B1, B2, B3, B4, B5, B6, B7, B1.

    Comportamento de navegação da guia

    Comportamento de navegação da guia "Local"

  • Uma vez O contêiner e todos os elementos filho recebem o foco uma vez. Para este exemplo, a ordem de tabulação é B1, B2, B7, B1 (a navegação interna com a tecla de seta também é demonstrada).

    Comportamento de navegação da guia

    Comportamento de navegação da guia "Uma vez"

  • Ciclo
    O foco volta para o elemento focalizável inicial dentro de um contêiner. Para este exemplo, a ordem de tabulação é B1, B2, B3, B4, B5, B6, B2...

    Comportamento de navegação da guia

    Comportamento de navegação da guia "Ciclo"

Aqui está o código para os exemplos anteriores (com 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 a ordem na qual os elementos recebem foco quando o usuário navega pelos controles usando a tecla Tab. Um controle com um índice de tabulação mais baixo recebe o foco antes de um controle com um índice mais alto.

Quando um controle não tem nenhum TabIndex especificado, ele recebe um valor de índice mais alto do que o valor de índice mais alto atual (e a prioridade mais baixa) de todos os controles interativos na árvore visual, com base no escopo.

Todos os elementos filho de um controle são considerados um escopo e, se um desses elementos também tiver elementos filho, eles serão considerados outro escopo. Qualquer ambiguidade é resolvida escolhendo o primeiro elemento na árvore visual do escopo.

Para excluir um controle da ordem de tabulação, defina a propriedade IsTabStop como false.

Substitua a ordem de tabulação padrão definindo a propriedade TabIndex .

Observação

TabIndex funciona da mesma maneira com UIElement.TabFocusNavigation e Control.TabNavigation.

Aqui, mostramos como a navegação de foco pode ser afetada pela propriedade TabIndex em elementos específicos.

Navegação por guia

Navegação por guia "Local" com comportamento TabIndex

No exemplo anterior, há dois escopos:

  • B1, área direcional (B2 - B6) e B7
  • área direcional (B2 - B6)

Quando B3 (na área direcional) obtém o foco, o escopo muda e a navegação por guias é transferida para a área direcional onde o melhor candidato para o foco subsequente é identificado. Nesse caso, B2 seguido por B4, B5 e B6. O escopo muda novamente e o foco se move para B1.

Aqui está o código para este exemplo.

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

Navegação direcional 2D para teclado, gamepad e controle remoto

Tipos de entrada sem ponteiro, como teclado, gamepad, controle remoto e ferramentas de acessibilidade, como o Narrador do Windows, compartilham um mecanismo subjacente comum para navegar e interagir com a interface do usuário do seu aplicativo Windows.

Nesta seção, abordamos como especificar uma estratégia de navegação preferencial e ajustar a navegação de foco em seu aplicativo por meio de um conjunto de propriedades de estratégia de navegação que dão suporte a todos os tipos de entrada baseados em foco e sem ponteiro.

Para obter mais informações gerais sobre como criar aplicativos e experiências para Xbox/TV, consulte Interação com o teclado, Projetando para Xbox e TV e Interações com gamepad e controle remoto.

As estratégias de navegação são aplicáveis ao teclado, gamepad, controle remoto e várias ferramentas de acessibilidade.

As propriedades de estratégia de navegação a seguir permitem que você influencie qual controle recebe o foco com base na tecla de seta, no botão direcional (D-pad) ou em um pressionado semelhante.

  • Estratégia de navegação XYFocusUpNavigation
  • Estratégia de navegação XYFocusDownNavigation
  • XYFocusLeftNavigationStrategy
  • XYFocusRightNavigationStrategy

Essas propriedades têm valores possíveis de Auto (padrão), NavigationDirectionDistance, Projection ou RectilinearDistance .

Se definido como Auto, o comportamento do elemento é baseado nos ancestrais do elemento. Se todos os elementos estiverem definidos como Automático, a projeção será usada.

Observação

Outros fatores, como o elemento focado anteriormente ou a proximidade com o eixo da direção de navegação, podem influenciar o resultado.

Projeção

A estratégia de projeção move o foco para o primeiro elemento encontrado quando a borda do elemento focado no momento é projetada na direção da navegação.

Neste exemplo, cada direção de navegação do foco é definida como Projeção. Observe como o foco se move para baixo de B1 para B4, ignorando B3. Isso ocorre porque, B3 não está na zona de projeção. Observe também como um candidato de foco não é identificado ao se mover para a esquerda de B1. Isso ocorre porque a posição de B2 em relação a B1 elimina B3 como candidato. Se B3 estivesse na mesma linha que B2, seria um candidato viável para navegação à esquerda. B2 é um candidato viável devido à sua proximidade desobstruída com o eixo da direção de navegação.

Estratégia de navegação de projeção

Estratégia de navegação de projeção

A estratégia NavigationDirectionDistance move o foco para o elemento mais próximo do eixo da direção de navegação.

A borda do retângulo delimitador correspondente à direção de navegação é estendida e projetada para identificar alvos candidatos. O primeiro elemento encontrado é identificado como o destino. No caso de vários candidatos, o elemento mais próximo é identificado como o alvo. Se ainda houver vários candidatos, o elemento mais alto/mais à esquerda será identificado como o candidato.

Estratégia de navegação NavigationDirectionDistance

Estratégia de navegação NavigationDirectionDistance

Distância retilínea

A estratégia RectilinearDistance move o foco para o elemento mais próximo com base na distância retilínea 2D (geometria do táxi).

A soma da distância primária e da distância secundária para cada candidato em potencial é usada para identificar o melhor candidato. Em um empate, o primeiro elemento à esquerda é selecionado se a direção solicitada for para cima ou para baixo, e o primeiro elemento para o topo é selecionado se a direção solicitada for esquerda ou direita.

Estratégia de navegação RectilinearDistance

Estratégia de navegação RectilinearDistance

Esta imagem mostra como, quando B1 tem foco e para baixo é a direção solicitada, B3 é o candidato ao foco RectilinearDistance. Isso se baseia nos seguintes cálculos para este exemplo:

  • Distância (B1, B3, Baixo) é 10 + 0 = 10
  • Distância (B1, B2, Baixo) é 0 + 40 = 30
  • A distância (B1, D, para baixo) é 30 + 0 = 30