Acessibilidade do teclado

Se seu aplicativo não oferecer um bom acesso ao teclado, usuários cegos ou com problemas de mobilidade terão dificuldade para usá-lo ou não poderão usá-lo.

Navegação de teclado entre elementos de interface de usuário

Para usar o teclado com um controle, o controle precisa ter foco e, para receber foco (sem usar um ponteiro), o controle precisa ser acessível em um projeto de interface de usuário da interface do usuário pela navegação por tabulação. Por padrão, a ordem de tabulação dos controles é a mesma ordem que eles foram adicionados à superfície de um projeto, listados em XAML ou adicionados programaticamente a um contêiner.

Na maioria dos casos, a ordem padrão baseada em como você define os controles no XAML é a melhor ordem, principalmente porque é a ordem de leitura dos leitores de tela. Contudo, a ordem padrão não corresponde necessariamente à ordem visual. A posição de exibição real poderia depender do contêiner de layout pai e de certas propriedades que você pode definir nos elementos filho para influenciar o layout. Para ter certeza de que seu aplicativo tem uma boa ordem de tabulação, teste esse comportamento. Principalmente se você tiver uma metáfora de grade ou de tabela em seu layout, a ordem de leitura dos usuários poderá ser diferente da ordem de tabulação. Nem sempre isso é um problema. No entanto, convém testar a funcionalidade do aplicativo como interface do usuário tanto de toque quanto acessível por teclado e verificar se ela responde igual para ambas as formas.

Você pode fazer com que a ordem de tabulação corresponda à ordem visual ajustando o XAML. Ou pode substituir a ordem de tabulação padrão definindo a propriedade TabIndex, conforme mostrado no exemplo a seguir de um layout de Grid que usa a navegação por guia de primeira coluna.

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>

Você pode desejar excluir um controle da ordem de tabulação. Você normalmente faz isso apenas tornando o controle não interativo, por exemplo, definindo sua propriedade IsEnabled como false. Um controle desabilitado é automaticamente excluído da ordem de tabulação. Às vezes, você pode precisar excluir um controle da ordem de tabulação, mesmo que ele não esteja desabilitado. Nesse caso, você pode definir a propriedade IsTabStop como false.

Todos os elementos que podem ter o foco estão geralmente na ordem de tabulação por padrão. A exceção é que determinados tipos de exibição de texto, como RichTextBlock, podem ter foco para serem acessados pela área de transferência para seleção de texto; no entanto, eles não estão na ordem de tabulação porque elementos de texto estáticos não costumam estar na ordem de tabulação. Normalmente, eles não são interativos (não podem ser invocados nem requerem entrada de texto, mas dão suporte ao Padrão de controle de texto que aceita a localização e o ajuste de pontos de seleção no texto). O texto não deve ter a conotação de que a definição de um foco para ele ativará alguma ação que seja possível. Os elementos de texto ainda serão detectados por tecnologias adaptativas, e lerão em voz alta em leitores de tela, mas isso depende de técnicas diferentes da busca desses elementos na ordem de tabulação prática.

Independentemente de você ajustar os valores de TabIndex ou usar a ordem padrão, estas regras são aplicáveis:

  • Se TabIndex não estiver definido em um elemento, o valor padrão usado será Int32.MaxValue e a ordem de tabulação será baseada na ordem de declaração nas coleções XAML ou filho.
  • Se TabIndex estiver definido em um elemento:
    • Elementos da interface do usuário com TabIndex igual a 0 são adicionados à ordem de tabulação com base na ordem de declaração no XAML ou nas coleções filhas.
    • Elementos da interface do usuário com TabIndex maior que 0 são adicionados à ordem de tabulação com base no valor de TabIndex.
    • Os elementos da interface do usuário com TabIndex menor que 0 são adicionados à ordem de tabulação e aparecem antes de qualquer valor zero. Isso é bem diferente de como o HTML manipula seu atributo tabindex (e não havia suporte a tabindex negativo nas especificações HTML antigas).

Por exemplo, o snippet a seguir mostra uma coleção de elementos com configurações variáveis de TabIndex (B recebe o valor de Int32.MaxValue ou 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>

Isso resulta na seguinte ordem de tabulação:

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

Navegação de teclado entre elementos de interface do usuário

Para os elementos compostos, é importante garantir uma navegação interior adequada entre os elementos contidos. Um elemento composto pode gerenciar seu filho ativo atual para reduzir a sobrecarga de ter todas os elementos filhos com capacidade de foco. Esse elemento composto é incluído na ordem de guias e trata os eventos de navegação de teclado sozinho. Muitos dos controles compostos já têm alguma lógica de navegação interna incorporada à manipulação de eventos do controle. Por exemplo, a passagem por seta-chave de itens é habilitada por padrão nos controles ListView, GridView, ListBox e FlipView.

Alternativas de teclado para ações do ponteiro e eventos para elementos de controle específico

Permita que os elementos de interface de usuário que podem ser clicados também possam ser invocados usando o teclado. Para usar o teclado com um elemento da interface do usuário, o elemento deve ter foco. Somente as classes que derivam de Control suportam o foco e a navegação de guias.

Para os elementos de interface do usuário que podem ser invocados, implemente manipuladores de eventos de teclado para as teclas de barra de espaço e Enter. Isso torna o suporte de acessibilidade básica de teclado completo e permite que os usuários utilizem cenários básicos de aplicativos apenas com o teclado, ou seja, os usuários podem acessar todos os elementos interativos da interface do usuário e ativar a funcionalidade padrão.

Em casos em que um elemento que você deseja usar na interface do usuário não pode ter foco, você pode criar seu próprio controle personalizado. Você deve definir a propriedade IsTabStop como true para habilitar o foco e deve fornecer uma indicação visual do estado do foco criando um estado visual que decora a interface do usuário com um indicador de foco. Contudo, é frequentemente mais fácil usar a composição de controle, para que o suporte para paradas de tabulação, foco e pares e padrões de Automação de Interface do Usuário da Microsoft possa ser tratados pelo controle no qual você escolhe compor o seu conteúdo.

Por exemplo, em vez de tratar um evento pressionado por ponteiro em um Image, você poderia encapsular esse elemento em um Button para conseguir suporte ao ponteiro, teclado e foco.

XAML

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

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

Atalhos do teclado

Além de implementar a navegação e ativação por teclado em seu aplicativo, é recomendável implementar atalhos para as funcionalidades do seu aplicativo. A navegação por tabulação fornece um bom nível básico de suporte ao teclado, mas com formulários complexos, você poderia desejar adicionar suporte às teclas de atalho também. Isso pode tornar o seu aplicativo mais eficiente para usar, mesmo para as pessoas que usam dispositivos de teclado e de ponteiro.

Um atalho é uma combinação de teclado que aumenta a produtividade fornecendo uma maneira eficiente para o usuário acessar a funcionalidade do aplicativo. Existem dois tipos de atalho:

  • Uma tecla de acesso é um atalho para um elemento da interface de usuário no seu aplicativo. As teclas de acesso consistem na tecla Alt, mais uma tecla de letra.
  • Uma tecla aceleradora é um atalho para um comando de aplicativo. Seu aplicativo pode ter uma interface do usuário que corresponde exatamente ao comando. As teclas aceleradoras consistem na tecla Ctrl, mais uma tecla de letra.

É fundamental oferecer aos usuários que dependem de leitores de tela e outras tecnologias assistenciais uma maneira fácil de descobrir as teclas de atalho do seu aplicativo. Comunique as teclas de atalho usando dicas de ferramentas, nomes acessíveis, descrições acessíveis ou alguma outra forma de comunicação na tela. No mínimo, as teclas de atalho devem ser bem documentadas no conteúdo de Ajuda do seu aplicativo.

Você pode documentar as teclas de acesso pelos leitores de tela definindo a propriedade anexada AutomationProperties.AccessKey para uma cadeia de caracteres que descreve a tecla de atalho. Há também uma propriedade anexada AutomationProperties.AcceleratorKey para documentar teclas de atalho não mnemônicas, apesar de leitores de tela geralmente tratarem as duas propriedades da mesma forma. Tente documentar as teclas de atalho de várias formas, usando dicas de ferramentas, propriedades de automação e documentação de Ajuda escrita.

O seguinte exemplo demonstra como documentar as teclas de atalho dos botões de reproduzir, pausar e parar mídia.

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

Definir AutomationProperties.AcceleratorKey ou AutomationProperties.AccessKey não habilita a funcionalidade de teclado. Ela só relata à estrutura de Automação da IU quais teclas devem ser usadas, para que essas informações possam ser repassadas aos usuários através de tecnologias assistivas. A implementação referente à manipulação de teclas ainda precisa ser feita em código, e não em XAML. Você ainda precisa anexar manipuladores para eventos KeyDown ou KeyUp no controle relevante para realmente implementar o comportamento de atalhos de teclado em seu aplicativo. Além disso, a decoração de texto sublinhado para uma tecla de acesso não é fornecida automaticamente. Você deve sublinhar explicitamente o texto para a tecla específica em seu mnemônico como formatação Underline embutida se desejar mostrar texto sublinhado na interface do usuário.

Para fins de simplicidade, o exemplo anterior omite o uso de recursos para cadeias de caracteres, como "Ctrl+A". Contudo, considere também as teclas de atalho durante o processo de localização. A localização de teclas de atalho é relevante porque a escolha da chave a ser usada como a chave de atalho normalmente depende do rótulo de texto visível para o elemento.

Para saber mais sobre as diretrizes de implementação de teclas de atalho, consulte Teclas de atalho nas Diretrizes de interação com a experiência do usuário do Windows.

Implementando um manipulador de eventos de chave

Os eventos de entrada, como eventos de chave, usam um conceito de evento denominado eventos roteados. Um evento roteado pode emergir por meio de elementos filho de um controle composto, de tal forma que um pai de controle comum possa manipular eventos para vários elementos filhos. Esse modelo de evento é conveniente para definir ações de teclas de atalho para um controle que contém várias partes compostas que não podem ter foco ou fazer parte da ordem de tabulação.

Para obter um código de exemplo que mostra como escrever um manipulador de eventos de tecla que inclui a verificação de modificadores como a tecla Ctrl, consulte Interações por teclado.

Navegação de teclado para controles personalizados

Recomendamos o uso de teclas de direção como atalhos de teclado para navegar entre os elementos filho, nos casos onde os elementos filho têm uma relação espacial entre si. Se os nós de exibição de árvore tiverem subelementos separados para lidar com expansão e recolhimento e ativação de nós, use as teclas de seta para esquerda e direita para obter a funcionalidade de expansão e recolhimento. Se você tem um controle orientado que oferece suporte à passagem direcional dentro do conteúdo de controle, use as teclas de direção adequadas.

Geralmente, você implementa a manipulação de chave personalizada para controles personalizados incluindo uma substituição dos métodos OnKeyDown e OnKeyUp como parte da lógica da classe.

Um exemplo do estado visual de um indicador de foco

Mencionamos anteriormente que qualquer controle personalizado que habilita o usuário a focá-lo deve ter um indicador de foco visual. Normalmente esse indicador de foco é tão simples quanto desenhar uma forma de retângulo imediatamente ao redor do retângulo delimitador normal do controle. O Rectangle de foco visual é um elemento de par no restante da composição do controle em um modelo de controle, mas é definido inicialmente com um Visibility valor de Collapsed porque o controle não tem foco ainda. Depois, quando o controle obtém o foco, é invocado um estado visual que define especificamente a Visibility do foco visual como Visible. Depois que o foco é movido para outro lugar, outro estado visual é chamado, e a Visibility se torna Collapsed.

Todos os controles XAML padrão exibem um indicador de foco visual adequado quando estão em foco (se puderem ser focalizados). Há também aparências potencialmente diferentes dependendo do tema escolhido do usuário (principalmente se o usuário estiver usando um modo de alto contraste.) Se estiver usando os controles XAML em sua interface do usuário e não substituir os modelos de controle, você não precisa fazer nada mais para obter os indicadores de foco visual nos controles que se comportam e exibem corretamente. Mas se você pretende criar um novo modelo para um controle ou se tiver curiosidade para saber como os controles XAML fornecem os indicadores de foco visual, o restante desta seção explica como isso é feito no XAML e na lógica de controle.

Aqui estão alguns exemplos de XAML provenientes do modelo XAML padrão para um Button.

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>

Até o momento, essa é apenas a composição. Para controlar a visibilidade do indicador do foco, defina os estados visuais que alternam a propriedade Visibility. Isso é feito usando o VisualStateManager e a propriedade anexada VisualStateManager.VisualStateGroups, conforme aplicado ao elemento raiz que define a composição.

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>

Observe como somente um dos estados nomeados ajusta a Visibility diretamente, enquanto os outros estão aparentemente vazios. A maneira como os estados visuais funcionam é que, tão logo o controle use outro estado da mesma classe VisualStateGroup, todas as animações aplicadas pelo estado anterior são canceladas imediatamente. Como a Visibility padrão da composição é Collapsed, isso significa que o retângulo não aparecerá. A lógica de controle controla isso escutando eventos de foco como GotFocus e mudando os estados com GoToState. Muitas vezes, isso já está manipulado para você, se você estiver usando um controle padrão ou personalizando com base em um controle que já tem esse comportamento.

Acessibilidade do teclado e o Windows Phone

Um dispositivo Windows Phone normalmente não tem um teclado de hardware dedicado. Porém, um SIP (Soft Input Panel) pode oferecer suporte a vários cenários de acessibilidade do teclado. Os leitores de tela podem ler entradas de texto no SIP Texto, incluindo exclusão de anúncios. Os usuários podem saber onde os seus dedos estão, pois o leitor de tela pode detectar se o usuário está usando as teclas e lê o nome da tecla pressionada em voz alta. Além disso, alguns dos conceitos de acessibilidade orientados pelo teclado podem ser mapeados com os comportamentos de tecnologia adaptativa relacionados que não usam teclado. Por exemplo, mesmo que um SIP não inclua uma tecla Tab, o Narrador reconhece um gesto de toque que é o equivalente a pressionar a tecla Tab, de modo que ter uma ordem de tabulação útil pelos controles em uma interface do usuário ainda é um princípio importante de acessibilidade. As teclas de setas que são usadas para navegar pelas partes dentro de controles complexos também são suportadas por meio de gestos de toque do Narrador. Quando o foco atinge um controle que não seja entrada de texto, o Narrador suporta um gesto que chama a ação desse controle.

Os atalhos do teclado não são normalmente relevantes para os aplicativos no Windows Phone porque um SIP não inclui as teclas Control ou Alt.

Exemplos

Dica

O aplicativo Galeria da WinUI 3 inclui exemplos interativos da maioria dos controles, recursos e funcionalidades da WinUI 3. Obtenha o aplicativo na Microsoft Store ou o código-fonte no GitHub