Eventos de teclado

Foco e eventos do teclado

Os seguintes eventos podem ocorrer para teclados físicos e virtuais.

Evento Descrição
KeyDown Ocorre quando uma tecla é pressionada.
KeyUp Ocorre quando uma tecla é liberada.

Importante

Alguns controles XAML lidam com eventos de entrada internamente. Nestes casos, pode parecer que não ocorre um evento de entrada, porque o ouvinte de evento não chama o manipulador associado. Geralmente, este subconjunto de teclas é processado pelo manipulador de classe para proporcionar suporte interno de acessibilidade de teclado básico. Por exemplo, a classe Button sobrepõe os eventos OnKeyDown para a tecla Enter ou a barra de espaço (bem como OnPointerPressed) e os encaminha para o evento Click do controle. Quando uma tecla é manipulada pela classe de controle, os eventos KeyDown e KeyUp não são acionados.
Isso fornece um teclado integrado equivalente para invocar o botão, semelhante a tocar com um dedo ou clicar com o mouse. Outras teclas, além da tecla Enter e da barra de espaço, ainda acionam os eventos KeyDown e KeyUp. Para obter mais informações sobre como a manipulação baseada em classes de eventos funciona (especificamente, a seção "Manipuladores de evento de entrada em controles), consulte Visão geral de eventos e eventos roteados.

Os controles em sua interface do usuário geram eventos de teclado apenas quando possuem foco de entrada. Um controle individual ganha foco quando o usuário clica ou toca diretamente nesse controle no layout, ou utiliza a tecla Tab para avançar na sequência de tabulação dentro da área de conteúdo.

Você também pode chamar um método Focus do controle para forçar o foco. Isso é necessário quando você implementa teclas de atalho porque o foco do teclado não está definido por padrão quando a interface do usuário é carregada. Para obter mais informações, consulte Exemplos de tecla de atalho posteriormente neste tópico.

Para um controle receber o foco de entrada, ele deve estar habilitado, visível e apresentar valores de propriedade IsTabStop e HitTestVisible como true. Esse é o estado padrão da maioria dos controles. Quando um controle tem um foco de entrada, ele pode acionar e responder a eventos de entrada de teclado conforme descrito posteriormente neste tópico. Você também pode responder a um controle que recebe ou perde foco manipulando os eventos GotFocus e LostFocus.

Por padrão, a sequência de tabulação de controles está na ordem que aparece na Extensible Application Markup Language (XAML). No entanto, é possível modificar essa ordem usando a propriedade TabIndex. Para saber mais, consulte Implementando a acessibilidade de teclado.

Manipuladores de eventos do teclado

Um manipulador de eventos de entrada implementa um delegado que fornece as seguintes informações:

  • O remetente do evento. O remetente relata o objeto ao qual o manipulador de eventos está anexado.
  • Dados do evento. Para eventos do teclado, esses dados serão uma instância de KeyRoutedEventArgs. O delegado para manipuladores é KeyEventHandler. As propriedades mais relevantes de KeyRoutedEventArgs para a maioria dos cenários de manipulação são Key e possivelmente KeyStatus.
  • OriginalSource. Como eventos do teclado são eventos roteados, os dados dos eventos fornecem OriginalSource. Se você deliberadamente permite que eventos subam a árvore de um objeto, OriginalSource é, por vezes, o objeto em questão em vez do remetente. No entanto, isso depende do seu design. Para saber mais sobre como você pode usar OriginalSource em vez do remetente, consulte a seção "Eventos roteados do teclado" deste tópico, ou Visão geral de eventos e eventos roteados.

Anexando um manipulador de eventos do teclado

Você pode anexar funções de manipulação de eventos do teclado a qualquer objeto que inclua o evento como um membro. Isso inclui qualquer classe derivada de UIElement. O exemplo de XAML a seguir mostra como anexar manipuladores ao evento KeyUp de um Grid.

<Grid KeyUp="Grid_KeyUp">
  ...
</Grid>

Você também pode anexar um manipulador de eventos em código. Para saber mais, confira Visão geral de eventos e eventos roteados.

Definindo um manipulador de eventos do teclado

O exemplo a seguir mostra a definição do manipulador de eventos incompleta para o manipulador de eventos KeyUp que foi anexado no exemplo anterior.

void Grid_KeyUp(object sender, KeyRoutedEventArgs e)
{
    //handling code here
}
Private Sub Grid_KeyUp(ByVal sender As Object, ByVal e As KeyRoutedEventArgs)
    ' handling code here
End Sub
void MyProject::MainPage::Grid_KeyUp(
  Platform::Object^ sender,
  Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
  {
      //handling code here
  }

Usando KeyRoutedEventArgs

Todos os eventos do teclado usam KeyRoutedEventArgs para dados de eventos, e KeyRoutedEventArg contém as seguintes propriedades:

Chaves virtuais

O evento KeyDown será acionado se a tecla for pressionada. Da mesma forma, KeyUp será acionado se a tecla for liberada. Normalmente, você escuta eventos para processar um valor de tecla específico. Para determinar qual tecla é pressionada ou liberada, verifique o valor de Key nos dados do evento. Key retorna um valor VirtualKey. A enumeração VirtualKey inclui todas as teclas com suporte.

Teclas modificadoras

As teclas modificadoras são teclas como Ctrl ou Shift que os usuários normalmente pressionam em combinação com outras teclas. Seu aplicativo pode usar essas combinações como atalhos de teclado personalizados para invocar comandos de aplicativo.

Observação

Para atalhos de teclado internos, consulte Teclas de acesso e aceleradores de teclado.

Você pode detectar combinações de teclas de atalho nos manipuladores de eventos KeyDown e KeyUp . Quando um evento de teclado ocorre para uma tecla não modificadora, você pode marcar se uma tecla modificadora está no estado pressionado.

Como alternativa, a função GetKeyState() do CoreWindow (obtida por meio de CoreWindow.GetForCurrentThread()) também pode ser usada para marcar estado do modificador quando uma tecla não modificadora é pressionada.

Os exemplos a seguir implementam esse segundo método, incluindo também o código stub para a primeira implementação.

Observação

A tecla Alt é representada pelo valor VirtualKey.Menu.

Amostra de teclas de atalho

O exemplo a seguir demonstra como implementar um conjunto de teclas de atalho personalizadas. Neste exemplo, os usuários podem controlar a reprodução de mídia usando os botões Play, Pause e Stop, ou os atalhos de teclado Ctrl+P, Ctrl+A e Ctrl+S. O botão XAML mostra os atalhos usando dicas de ferramentas e propriedades AutomationProperties nos rótulos dos botões. Esta autodocumentação é importante para aumentar a usabilidade e acessibilidade de seu aplicativo. Para obter mais informações, consulte Acessibilidade do teclado.

Observe também que a página define o foco de entrada para si própria quando é carregada. Sem essa etapa, nenhum controle terá foco de entrada inicial e o aplicativo não acionará eventos de entrada até que o usuário defina o foco de entrada manualmente (por exemplo, pressionando TAB ou clicando em um controle).

<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>
//showing implementations but not header definitions
void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
{
    (void) e;    // Unused parameter
    this->Loaded+=ref new RoutedEventHandler(this,&amp;MainPage::ProgrammaticFocus);
}
void MainPage::ProgrammaticFocus(Object^ sender, RoutedEventArgs^ e) 
{
    this->Focus(Windows::UI::Xaml::FocusState::Programmatic);
}

void KeyboardSupport::MainPage::MediaButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    FrameworkElement^ fe = safe_cast<FrameworkElement^>(sender);
    if (fe->Name == "PlayButton") {DemoMovie->Play();}
    if (fe->Name == "PauseButton") {DemoMovie->Pause();}
    if (fe->Name == "StopButton") {DemoMovie->Stop();}
}


bool KeyboardSupport::MainPage::IsCtrlKeyPressed()
{
    auto ctrlState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::Control);
    return (ctrlState & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down;
}

void KeyboardSupport::MainPage::Grid_KeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
{
    if (e->Key == VirtualKey::Control) isCtrlKeyPressed = true;
}


void KeyboardSupport::MainPage::Grid_KeyUp(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e)
{
    if (IsCtrlKeyPressed()) 
    {
        if (e->Key==VirtualKey::P) { DemoMovie->Play(); }
        if (e->Key==VirtualKey::A) { DemoMovie->Pause(); }
        if (e->Key==VirtualKey::S) { DemoMovie->Stop(); }
    }
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    // Set the input focus to ensure that keyboard events are raised.
    this.Loaded += delegate { this.Focus(FocusState.Programmatic); };
}

private void MediaButton_Click(object sender, RoutedEventArgs e)
{
    switch ((sender as Button).Name)
    {
        case "PlayButton": DemoMovie.Play(); break;
        case "PauseButton": DemoMovie.Pause(); break;
        case "StopButton": DemoMovie.Stop(); break;
    }
}

private static bool IsCtrlKeyPressed()
{
    var ctrlState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control);
    return (ctrlState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down;
}

private void Grid_KeyDown(object sender, KeyRoutedEventArgs e)
{
    if (IsCtrlKeyPressed())
    {
        switch (e.Key)
        {
            case VirtualKey.P: DemoMovie.Play(); break;
            case VirtualKey.A: DemoMovie.Pause(); break;
            case VirtualKey.S: DemoMovie.Stop(); break;
        }
    }
}
Private isCtrlKeyPressed As Boolean
Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)

End Sub

Private Function IsCtrlKeyPressed As Boolean
    Dim ctrlState As CoreVirtualKeyStates = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control);
    Return (ctrlState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down;
End Function

Private Sub Grid_KeyDown(sender As Object, e As KeyRoutedEventArgs)
    If IsCtrlKeyPressed() Then
        Select Case e.Key
            Case Windows.System.VirtualKey.P
                DemoMovie.Play()
            Case Windows.System.VirtualKey.A
                DemoMovie.Pause()
            Case Windows.System.VirtualKey.S
                DemoMovie.Stop()
        End Select
    End If
End Sub

Private Sub MediaButton_Click(sender As Object, e As RoutedEventArgs)
    Dim fe As FrameworkElement = CType(sender, FrameworkElement)
    Select Case fe.Name
        Case "PlayButton"
            DemoMovie.Play()
        Case "PauseButton"
            DemoMovie.Pause()
        Case "StopButton"
            DemoMovie.Stop()
    End Select
End Sub

Observação

A configuração de AutomationProperties.AcceleratorKey ou AutomationProperties.AccessKey no XAML oferece informações sobre cadeia de caracteres, que documentam a tecla de atalho para chamar a ação em particular. As .informações são capturadas por clientes de Automação da Interface do Usuário da Microsoft como o Narrador e são tipicamente fornecidas diretamente ao usuário.

A configuração de AutomationProperties.AcceleratorKey ou de AutomationProperties.AccessKey não tem qualquer ação por conta própria. Você ainda precisa anexar manipuladores para eventos KeyDown ou KeyUp para realmente implementar o comportamento de atalho 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.

 

Eventos de teclado roteados

Alguns eventos são eventos roteados, inclusive KeyDown e KeyUp. Eventos roteados usam a estratégia de roteamento por propagação. A estratégia de roteamento por propagação indica que um evento se origina de um objeto filho e então é encaminhado para sucessivos objetos pai na árvore de objetos. Isso apresenta outra oportunidade para manipular o mesmo evento e interagir com os mesmos dados de eventos.

Considere o exemplo em XAML a seguir, que manipula eventos KeyUp para um objeto Canvas e dois objetos Button. Neste caso, se você soltar uma tecla enquanto o foco é mantido por cada objeto Button, ele aciona o evento KeyUp. O evento é, então, propagado para o Canvas pai.

<StackPanel KeyUp="StackPanel_KeyUp">
  <Button Name="ButtonA" Content="Button A"/>
  <Button Name="ButtonB" Content="Button B"/>
  <TextBlock Name="statusTextBlock"/>
</StackPanel>

O exemplo a seguir mostra como implementar o manipulador de eventos KeyUp no conteúdo XAML correspondente do exemplo anterior.

void StackPanel_KeyUp(object sender, KeyRoutedEventArgs e)
{
    statusTextBlock.Text = String.Format(
        "The key {0} was pressed while focus was on {1}",
        e.Key.ToString(), (e.OriginalSource as FrameworkElement).Name);
}

Observe o uso da propriedade OriginalSource no manipulador anterior. Aqui, OriginalSource relata o objeto que acionou o evento. O objeto não poderia ser o StackPanel, pois o StackPanel não é um controle e não pode ter foco. Apenas um dos dois botões dentro de StackPanel poderiam ter possivelmente acionado o evento, mas qual deles? Você usa OriginalSource para distinguir o objeto de origem do evento real se estiver manipulando o evento em um objeto pai.

A propriedade Handled nos dados do evento

Dependendo da sua estratégia de manipulação de eventos, você pode querer que apenas um manipulador de eventos reaja a um evento propagado. Por exemplo, se você tiver um manipulador KeyUp específico anexado a um dos controles Button, ele terá a mesma oportunidade de manipular o evento. Nesse caso, talvez você não queira que o painel pai manipule também o evento. Nesse cenário, use a propriedade Handled nos dados do evento.

O objetivo da propriedade Handled em uma classe de dados de eventos roteados é informar que outro manipulador registrado por você anteriormente na rota do evento já atuou. Isso influencia o comportamento do sistema de eventos roteados. Quando você define Handled como true em um manipulador de eventos, o roteamento desse evento para, e ele não é enviado para os elementos pais sucessivos.

AddHandler e eventos do teclado já manipulados

Você pode usar uma técnica especial para anexar manipuladores que podem atuar em eventos que já estão marcados como manipulados. Essa técnica usa o método AddHandler para registrar um manipulador, em vez de usar atributos XAML ou sintaxe específica da linguagem para adicionar manipuladores, como += em C#.

Uma limitação geral dessa técnica é que a API AddHandler obtém um parâmetro do tipo RoutedEvent, que identifica o evento roteado em questão. Nem todos os eventos roteados fornecem um identificador RoutedEvent, e essa consideração afeta os eventos roteados que podem ainda ser manipulados no caso Handled. Os eventos KeyDown e KeyUp possuem identificadores de eventos roteados (KeyDownEvent e KeyUpEvent) em UIElement. Contudo, outros eventos, como TextBox.TextChanged, não possuem identificadores de eventos roteados e, portanto, não podem ser usados com a técnica AddHandler.

Substituindo eventos do teclado e o comportamento

Você pode substituir eventos-chave para controles específicos (como GridView) para fornecer navegação de foco consistente para vários dispositivos de entrada, incluindo teclado e gamepad.

No exemplo a seguir, subclasse o controle e substituimos o comportamento KeyDown para mover o foco para o conteúdo gridView quando qualquer tecla de seta for pressionada.

  public class CustomGridView : GridView
  {
    protected override void OnKeyDown(KeyRoutedEventArgs e)
    {
      // Override arrow key behaviors.
      if (e.Key != Windows.System.VirtualKey.Left && e.Key !=
        Windows.System.VirtualKey.Right && e.Key !=
          Windows.System.VirtualKey.Down && e.Key !=
            Windows.System.VirtualKey.Up)
              base.OnKeyDown(e);
      else
        FocusManager.TryMoveFocus(FocusNavigationDirection.Down);
    }
  }

Observação

Se usar um GridView apenas para layout, considere usar outros controles, como ItemsControl com ItemsWrapGrid.

Comando

Um pequeno número de elementos da interface do usuário fornece suporte interno para comandos. Comandos usam eventos roteados relacionados à entrada na sua implementação subjacente. Eles permitem o processamento de entrada relacionada da interface do usuário, como uma determinada ação do ponteiro ou uma tecla de aceleração específica, invocando um único manipulador de comandos.

Se comandos estiverem disponíveis para um elemento da interface do usuário, considere usar as respectivas APIs de comando em vez de qualquer evento de entrada à parte. Para obter mais informações, consulte ButtonBase.Command.

Também é possível implementar ICommand para encapsular a funcionalidade do comando que você invoca de manipuladores de eventos comuns. Isso permite usar comandos mesmo quando não há nenhuma propriedade Command disponível.

Entrada de texto e controles

Certos controles reagem a eventos do teclado com a sua própria manipulação. Por exemplo, TextBox é um controle projetado para capturar e depois representar visualmente o texto que foi inserido com o uso de um teclado. Ele usa KeyUp e KeyDown na sua própria lógica, para capturar toques de tecla, depois ativa também seu próprio evento TextChanged caso o texto seja realmente alterado.

Geralmente, você ainda pode adicionar manipuladores para KeyUp e KeyDown a uma TextBox, ou a qualquer controle relacionado feito para processar entrada de texto. Contudo, de acordo com o seu design, um controle pode não responder a todos os valores de teclas a ele direcionados por eventos de teclas. Cada controle tem um comportamento específico.

Como exemplo, ButtonBase (a classe base para Button) processa KeyUp para que possa verificar a barra de espaço ou a tecla Enter. ButtonBase considera KeyUp equivalente a um botão esquerdo do mouse apertado para emitir um evento Click. Esse processamento do evento é realizado quando ButtonBase substitui o método virtual OnKeyUp. Em sua implementação, ele define Handled como true. O resultado é que qualquer pai de um botão que estiver ouvindo um evento de tecla, no caso de uma barra de espaço, não receberá o evento já manipulado pelos próprios manipuladores.

Outro exemplo é TextBox. Algumas teclas, como as teclas de direção, não são consideradas texto por TextBox e, em vez disso, são consideradas específicas para o comportamento da interface do usuário de controle. TextBox marca esses casos de evento como manipulados.

Os controles personalizados podem implementar seu próprio comportamento de substituição semelhante para eventos-chave substituindo OnKeyDown / OnKeyUp. Se o seu controle personalizado processa teclas aceleradoras específicas ou tem um comportamento de controle ou foco semelhante ao cenário descrito para TextBox, você deve colocar essa lógica nas próprias substituições de OnKeyDown / OnKeyUp.

O teclado virtual

Os controles de entrada de texto oferecem suporte automático para o teclado virtual. Quando o usuário define o foco de entrada como um controle de texto usando a entrada por toque, o teclado virtual aparece automaticamente. Quando o foco de entrada não está em um controle de texto, o teclado virtual é ocultado.

Quando o teclado virtual aparece, ele reposiciona automaticamente sua interface de usuário para garantir que elemento focado permaneça visível. Isso pode fazer com que outras áreas importantes de sua interface de usuário se movam para fora da tela. No entanto, você pode desabilitar o comportamento padrão e fazer seus próprios ajustes de interface de usuário quando o teclado virtual aparece. Para saber mais, veja a Amostra de teclado virtual.

Se você criar um controle personalizado que exige entrada de texto, mas não deriva de um controle de entrada de texto padrão, será possível adicionar suporte de teclado virtual implementando os padrões corretos de controle de Automação da Interface do Usuário. Para saber mais, veja a Amostra de teclado virtual.

O pressionamento das teclas no teclado virtual aciona os eventos KeyDown e KeyUp assim como o pressionamento das teclas no teclado físico. No entanto, o teclado virtual não acionará eventos de entrada para Ctrl+A, Ctrl+Z, Ctrl+X, Ctrl+C e Ctrl+V, os quais são reservados para a manipulação de texto no controle de entrada.

Você pode tornar a entrada de dados muito mais rápida e fácil para os usuários em seu aplicativo definindo o escopo de entrada do controle de texto para corresponder ao tipo de dados que o usuário deve inserir. O escopo de entrada oferece uma dica sobre o tipo de entrada de texto esperado pelo controle, para que o sistema possa fornecer um layout de teclado virtual especializado para o tipo de entrada. Por exemplo, se uma caixa de texto for usada apenas para inserir um PIN de 4 dígitos, defina a propriedade InputScope como Number. Isso informa ao sistema para mostrar o layout do teclado numérico, facilitando a inserção do PIN. Para obter mais detalhes, consulte Usar o escopo de entrada para alterar o teclado virtual.

Desenvolvedores

Designers

Exemplos

Exemplos de arquivo morto