Partilhar via


Foco de navegação para teclado, comando de jogo, comando remoto e ferramentas de acessibilidade

Teclado, comando e D-pad

Use a Focus Navigation para proporcionar experiências de interação abrangentes e consistentes nas suas aplicações Windows e controlos personalizados para utilizadores avançados de teclado, pessoas com deficiência e outros requisitos de acessibilidade, bem como a experiência de 3 metros dos ecrãs de televisão e da Xbox One.

Visão geral

A navegação por foco refere-se ao mecanismo subjacente que permite aos utilizadores navegar e interagir com a interface de uma aplicação Windows através de um teclado, comando de jogo ou controlo remoto.

Observação

Os dispositivos de entrada são tipicamente classificados como dispositivos de apontamento, como táteis, touchpad, caneta e rato, e dispositivos não apontantes, como teclado, comando de jogo e controlo remoto.

Este tópico descreve como otimizar uma aplicação Windows e criar experiências de interação personalizadas para utilizadores que dependem de tipos de entrada não apontados.

Apesar de nos focarmos na introdução do teclado para controlos personalizados em aplicações Windows em PCs, uma experiência de teclado bem desenhada também é importante para teclados de software como o teclado tátil e o Teclado On-Screen (OSK), que suportam ferramentas de acessibilidade como o Windows Narrator e suportam a experiência de 3 metros.

Consulte Handle pointer input para orientações sobre como criar experiências personalizadas em aplicações Windows para dispositivos apontadores.

Para informações mais gerais sobre a criação de aplicações e experiências para teclado, consulte Interação com Teclado.

Orientações gerais

Apenas os elementos da interface que requerem interação do utilizador devem suportar a navegação no foco, enquanto os elementos que não exigem ação, como imagens estáticas, não precisam de foco do teclado. Leitores de ecrã e ferramentas de acessibilidade semelhantes continuam a anunciar estes elementos estáticos, mesmo quando não estão incluídos na navegação de foco.

É importante lembrar que, ao contrário da navegação com um dispositivo ponteiro como rato ou tátil, a navegação de foco é linear. Ao implementar a navegação por foco, considere como o utilizador irá interagir com a sua aplicação e qual deve ser a navegação lógica. Na maioria dos casos, recomendamos que o comportamento de navegação por foco personalizado siga o padrão de leitura preferido da cultura do utilizador.

Outras considerações sobre a navegação de foco incluem:

  • Os controlos estão agrupados logicamente?
  • Existem grupos de controlos com maior importância?
    • Se sim, esses grupos contêm subgrupos?
  • O layout requer navegação direcional personalizada (teclas de seta) e ordem de tabulação?

O eBook de Software de Engenharia para Acessibilidade tem um excelente capítulo sobre Desenhar a Hierarquia Lógica.

Navegação direcional 2D para teclado

A região interna de navegação 2D de um controlo, ou de um grupo de controlo, é referida como a sua "área direcional". Quando o foco se desloca para este objeto, as setas do teclado (esquerda, direita, cima e baixo) podem ser usadas para navegar entre os sub-elementos dentro da área de direção.

área direcional 2D Região interna de navegação, ou área direcional, de um grupo de controlo

Pode usar a propriedade XYFocusKeyboardNavigation (que tem possíveis valores de Auto, Ativado ou Desativado) para gerir a navegação interna 2D com as setas do teclado.

Observação

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

Auto (comportamento padrão)

Quando definido para Auto, o comportamento de navegação direcional é determinado pela ascendência do elemento, ou hierarquia de herança. Se todos os antepassados estiverem em modo padrão (definido para Auto), a navegação direcional com o teclado não é suportada.

Disabled

Defina XYFocusKeyboardNavigation como Desativado para bloquear a navegação direcional para o controlo e os seus elementos filhos.

comportamento desativado do XYFocusNavegaçãoPorTeclado comportamento desativado do XYFocusNavegaçãoPorTeclado

Neste exemplo, o StackPanel principal (ContainerPrimary) tem o XYFocusKeyboardNavigation definido como Ativado. Todos os elementos filhos herdam esta definição e podem ser percorridos com as teclas de seta. No entanto, os elementos B3 e B4 estão num StackPanel secundário (ContainerSecondary) com XYFocusKeyboardNavigation definido para Desativado, que sobrepõe o contentor primário e desativa a navegação por teclas de seta nela e entre os seus elementos filhos.

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

Ativado

Defina XYFocusKeyboardNavigation como Ativado para suportar a navegação direcional 2D para um controlo e cada um dos seus elementos filho UIElement.

Quando definido, a navegação com as setas está restrita a elementos dentro da área direcional. A navegação por abas não é afetada, pois todos os controlos permanecem acessíveis através da sua ordem hierárquica de abas.

Comportamento ativado do XYFocusKeyboardNavigation Comportamento ativado do XYFocusKeyboardNavigation

Neste exemplo, o StackPanel principal (ContainerPrimary) tem o XYFocusKeyboardNavigation definido como Ativado. Todos os elementos filhos herdam esta definição e podem ser navegados usando as teclas de seta. Os elementos B3 e B4 encontram-se num StackPanel secundário (ContainerSecondary) onde XYFocusKeyboardNavigation não está definido, que então herda a configuração principal do contentor. O elemento B5 não está dentro de uma área direcional declarada e não suporta navegação por teclas de seta, mas suporta o comportamento padrão de navegação por separador.

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

É possível ter áreas direcionais aninhadas em vários níveis. Se todos os elementos progenitores tiverem XYFocusKeyboardNavigation definido como Ativado, os limites das regiões de navegação interna são ignorados.

Aqui está um exemplo de duas áreas direcionais aninhadas dentro de um elemento que não suporta explicitamente navegação direcional 2D. Neste caso, a navegação direcional não é suportada entre as duas áreas aninhadas.

XYFocusKeyboardNavigation ativado e comportamento aninhado XYFocusKeyboardNavigation ativado e comportamento aninhado

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

  • Quando B1 tem foco, apenas B5 pode ser acessado (e vice-versa) porque há um limite de área direcional onde o XYFocusKeyboardNavigation está configurado como Desativado, tornando B2, B3 e B4 inacessíveis com as teclas de seta.
  • Quando B2 tem foco, só se pode navegar até B3 (e vice-versa) porque o limite da área direcional impede a navegação com as setas para B1, B4 e B5.
  • Quando o B4 tem foco, a tecla Tab deve ser usada para navegar entre os controlos

XYFocusKeyboardNavigation ativado e comportamento aninhado complexo

XYFocusKeyboardNavigation ativado e comportamento aninhado complexo

Navegação por separadores

Enquanto as teclas de seta podem ser usadas para navegação direcional 2D num controlo ou grupo de controlo, a tecla Tab pode ser usada para navegar entre todos os controlos numa aplicação Windows.

Todos os controlos interativos suportam a navegação por teclas de tabulação por defeito (as propriedades IsEnabled e IsTabStopsão verdadeiras), com a ordem lógica de tabulação derivada do layout de controlos na sua aplicação. No entanto, a ordem padrão não corresponde necessariamente à ordem visual. A posição real do ecrã pode depender do contentor do layout pai e de certas propriedades que pode definir nos elementos filhos para influenciar o layout.

Evite uma ordem de tabulação personalizada que faça o foco saltar na sua aplicação. Por exemplo, uma lista de controlos num formulário deve ter uma ordem de tabulação que flui de cima para baixo e da esquerda para a direita (dependendo da localização).

Nesta secção, descrevemos como esta ordem de tabulação pode ser totalmente personalizada para se adequar à sua aplicação.

Definir o comportamento da navegação em separadores

A propriedade TabFocusNavigation do UIElement especifica o comportamento de navegação por separadores para toda a sua árvore de objetos (ou área de orientação).

Observação

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

Como mencionámos na secção anterior, para evitar uma experiência de navegação confusa, recomendamos que os elementos-filho de uma área direcional não sejam explicitamente especificados na ordem de navegação por separadores da sua aplicação. Consulte as propriedades UIElement.TabFocusNavigation e TabIndex para mais detalhes sobre o comportamento de tabulação de um elemento.

Para versões do Windows anteriores ao 10 Creators Update (build 10.0.15063), as definições de separadores estavam limitadas a objetos ControlTemplate. Para mais informações, consulte Control.TabNavigation.

TabFocusNavigation tem um valor do tipo KeyboardNavigationMode , com os seguintes valores possíveis (note que estes exemplos não são grupos de controlo personalizados e não requerem navegação interna com as setas):

  • Os índices de tabulação locais (por defeito) são reconhecidos na subárvore local dentro do contentor. Neste exemplo, a ordem de tabulação é B1, B2, B3, B4, B5, B6, B7, B1.

    O comportamento de navegação do separador

    Comportamento de navegação na aba "Local"

  • Uma vez O contentor e todos os elementos filhos recebem foco uma vez. Neste exemplo, a ordem de tabulação é B1, B2, B7, B1 (também é demonstrada a navegação interna com tecla de seta).

    Comportamento de navegação por abas

    Comportamento de navegação de abas "Once"

  • Ciclo
    O foco volta ao elemento focal inicial dentro de um recipiente. Neste exemplo, a ordem de tabulação é B1, B2, B3, B4, B5, B6, B2...

    Comportamento de navegação no separador

    Comportamento de navegação no separador "Cycle"

Aqui está o código dos 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 o TabIndex para especificar a ordem em que os elementos recebem foco quando o utilizador navega pelos controlos usando a tecla Tab. Um controlo com um índice de tabulação mais baixo recebe foco antes de um controlo com índice mais alto.

Quando um controlo não tem TabIndex especificado, é-lhe atribuído um valor de índice superior ao valor de índice mais alto atual (e a prioridade mais baixa) de todos os controlos interativos na árvore visual, com base no âmbito.

Todos os elementos filhos de um controlo são considerados um âmbito e, se um destes elementos também tiver elementos filhos, estes são considerados outro âmbito. Qualquer ambiguidade é resolvida escolhendo o primeiro elemento na árvore visual do escopo.

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

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

Observação

O TabIndex funciona da mesma forma tanto com UIElement.TabFocusNavigation como com Control.TabNavigation.

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

Navegação na aba

Navegação por separadores "Local" com comportamento do TabIndex

No exemplo anterior, existem dois âmbitos:

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

Quando o B3 (na área direcional) recebe o foco, o escopo muda e a navegação por tabulação transfere-se para a área direcional, onde se identifica o melhor candidato para receber o foco posteriormente. Neste caso, B2 seguido de B4, B5 e B6. O âmbito muda novamente e o foco passa para B1.

Aqui está o código deste 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, comando para jogos e comando remoto

Tipos de entrada sem ponteiro, como teclado, comando, controlo remoto e ferramentas de acessibilidade como o Narrador do Windows, partilham um mecanismo comum e subjacente para navegar e interagir com a interface da sua aplicação Windows.

Nesta secção, abordamos como especificar uma estratégia de navegação preferencial e afinar a navegação de foco dentro da sua aplicação através de um conjunto de propriedades de estratégia de navegação que suportam todos os tipos de entrada baseados em foco, sem ponteiros.

Para informações mais gerais sobre a criação de aplicações e experiências para Xbox/TV, veja Interação por Teclado, Conceção para Xbox e TV, e Interações com Gamepad e Comandos Remotos.

As estratégias de navegação são aplicáveis ao teclado, gamepad, comando à distância e várias ferramentas de acessibilidade.

As seguintes propriedades de estratégia de navegação permitem influenciar qual o controlo que recebe o foco com base na tecla de seta, no botão direcional (D-pad) ou outro botão pressionado.

  • EstratégiaDeNavegaçãoXYFocusParaCima
  • XYFocusDownNavigationStrategy
  • XYFocusLeftNavigationStrategy
  • XYFocusRightNavigationStrategy

Estas propriedades têm valores possíveis de Auto (por defeito), NavigationDirectionDistance, Projection ou RectilinearDistance .

Se definido para Auto, o comportamento do elemento baseia-se nos antepassados do elemento. Se todos os elementos estiverem definidos para Auto, é usada Projeção .

Observação

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

Projection

A estratégia de Projeção move o foco para o primeiro elemento encontrado quando a aresta do elemento atualmente focado é projetada na direção da navegação.

Neste exemplo, cada direção de navegação de foco está definida como Projeção. Repare como o foco desce de B1 para B4, contornando B3. Isto porque B3 não está na zona de projeção. Repara também como não é identificado um candidato de foco ao mover-se para a esquerda desde o B1. Isto porque a posição de B2 em relação a B1 elimina B3 como candidato. Se B3 estivesse na mesma fila que B2, seria um candidato viável para navegação à esquerda. B2 é um candidato viável devido à sua proximidade desobstruída ao eixo de direção de navegação.

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

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

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

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

NavegaçãoDireçãoEstratégia de navegação a distância

NavegaçãoDireçãoEstratégia de navegação a distância

Distância Retilinear

A estratégia de Distância Retilínea 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 a cada candidato potencial é usada para identificar o melhor candidato. Em 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 por distância rectilinear

Estratégia de navegação por distância rectilinear

Esta imagem mostra que, quando B1 está em foco e a direção solicitada é para baixo, B3 é o candidato a foco em RectilinearDistance. Isto baseia-se nas seguintes calculações para este exemplo:

  • A distância (B1, B3, Down) é 10 + 0 = 10
  • A distância (B1, B2, Down) é 0 + 40 = 30
  • A distância (B1, D, Down) é 30 + 0 = 30