Partilhar via


Eventos de teclado

Eventos de teclado e foco

Os eventos de teclado a seguir podem ocorrer para teclados de hardware e de toque.

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. Nesses casos, pode parecer que um evento de entrada não ocorre porque o ouvinte de eventos não invoca o manipulador associado. Normalmente, esse subconjunto de teclas é processado pelo manipulador de classe para fornecer suporte interno à acessibilidade básica do teclado. Por exemplo, a classe Button substitui os eventos OnKeyDown para a tecla Espaço e a tecla Enter (bem como OnPointerPressed) e os roteia para o evento Click do controle. Quando um pressionamento de tecla é manipulado pela classe de controle, os eventos KeyDown e KeyUp não são gerados.
Isso fornece um teclado integrado equivalente para invocar o botão, semelhante a tocar nele com um dedo ou clicar nele com um mouse. Teclas diferentes de Espaço ou Enter ainda disparam eventos KeyDown e KeyUp . Para obter mais informações sobre como funciona a manipulação de eventos baseada em classe (especificamente, a seção "Manipuladores de eventos 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 somente quando têm foco de entrada. Um controle individual ganha foco quando o usuário clica ou toca diretamente nesse controle no layout ou usa a tecla Tab para entrar em uma sequência de guias dentro da área de conteúdo.

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

Para que um controle receba o foco de entrada, ele deve estar habilitado, visível e ter os valores de propriedade IsTabStop e HitTestVisible de true. Esse é o estado padrão para a maioria dos controles. Quando um controle tem foco de entrada, ele pode gerar e responder a eventos de entrada do teclado, conforme descrito posteriormente neste tópico. Você também pode responder a um controle que está recebendo ou perdendo o foco manipulando os eventos GotFocus e LostFocus .

Por padrão, a sequência de guias de controles é a ordem em que eles aparecem no XAML (Extensible Application Markup Language). No entanto, você pode modificar essa ordem usando a propriedade TabIndex . Para obter mais informações, consulte Implementando a acessibilidade do teclado.

Manipuladores de eventos de 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 de 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 manipulador são Key e, possivelmente, KeyStatus.
  • Fonte original. Como os eventos de teclado são eventos roteados, os dados do evento fornecem OriginalSource. Se você permitir deliberadamente que os eventos borbulhem por meio de uma árvore de objetos, OriginalSource às vezes será o objeto de preocupação em vez do remetente. No entanto, isso depende do seu design. Para obter mais informações sobre como você pode usar OriginalSource em vez de remetente, consulte a seção "Eventos roteados de teclado" deste tópico ou Visão geral de eventos e eventos roteados.

Anexando um manipulador de eventos de teclado

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

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

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

Definindo um manipulador de eventos de teclado

O exemplo a seguir mostra a definição incompleta do manipulador de eventos 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 de teclado usam KeyRoutedEventArgs para dados de evento, e KeyRoutedEventArgs contém as seguintes propriedades:

Chaves virtuais

O evento KeyDown é gerado se uma tecla for pressionada. Da mesma forma, o KeyUp é gerado se uma chave for liberada. Normalmente, você ouve os eventos para processar um valor de chave específico. Para determinar qual tecla é pressionada ou liberada, verifique o valor da chave nos dados do evento. Key retorna um valor VirtualKey . A enumeração VirtualKey inclui todas as chaves 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 do 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 ocorre um evento de teclado para uma tecla não modificadora, você pode verificar 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 verificar o 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 de stub para a primeira implementação.

Observação

A tecla Alt é representada pelo valor VirtualKey.Menu .

Exemplo 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 Reproduzir, Pausar e Parar ou os atalhos de teclado Ctrl+P, Ctrl+A e Ctrl+S. O XAML do botão mostra os atalhos usando dicas de ferramenta e propriedades AutomationProperties nos rótulos do botão. Essa autodocumentação é importante para aumentar a usabilidade e a acessibilidade do 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 mesma quando é carregada. Sem essa etapa, nenhum controle tem foco de entrada inicial e o aplicativo não gera eventos de entrada até que o usuário defina o foco de entrada manualmente (por exemplo, usando a tecla 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 em XAML fornece informações de cadeia de caracteres, que documentam a tecla de atalho para invocar essa ação específica. As informações são capturadas por clientes de automação da interface do usuário da Microsoft, como o Narrador, e normalmente são fornecidas diretamente ao usuário.

A configuração de AutomationProperties.AcceleratorKey ou AutomationProperties.AccessKey não tem nenhuma ação por conta própria. Você ainda precisará 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 chave de acesso não é fornecida automaticamente. Você deve sublinhar explicitamente o texto da chave específica em seu mnemônico como formatação de sublinhado embutida se desejar mostrar o texto sublinhado na interface do usuário.

 

Eventos roteados pelo teclado

Determinados eventos são eventos roteados, incluindo KeyDown e KeyUp. Os eventos roteados usam a estratégia de roteamento de propagação. A estratégia de roteamento de propagação significa que um evento se origina de um objeto filho e, em seguida, é roteado para objetos pai sucessivos na árvore de objetos. Isso apresenta outra oportunidade de manipular o mesmo evento e interagir com os mesmos dados de evento.

Considere o exemplo XAML a seguir, que manipula eventos KeyUp para um Canvas e dois objetos Button. Nesse caso, se você soltar uma tecla enquanto o foco estiver pressionado por um dos objetos Button , ele acionará o evento KeyUp . O evento é então borbulhado 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 para o conteúdo XAML correspondente no 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 gerou o evento. O objeto não pode ser o StackPanel porque o StackPanel não é um controle e não pode ter foco. Apenas um dos dois botões dentro do StackPanel poderia ter gerado o evento, mas qual? 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, talvez você queira que apenas um manipulador de eventos reaja a um evento de propagação. Por exemplo, se você tiver um manipulador KeyUp específico anexado a um dos controles Button, ele terá a primeira oportunidade de manipular esse evento. Nesse caso, talvez você não queira que o painel pai também manipule o evento. Para esse cenário, você pode usar a propriedade Handled nos dados do evento.

A finalidade da propriedade Handled em uma classe de dados de evento roteado é relatar que outro manipulador que você registrou anteriormente na rota de evento já agiu. Isso influencia o comportamento do sistema de eventos roteado. Quando você define Handled como true em um manipulador de eventos, esse evento interrompe o roteamento e não é enviado para elementos pai sucessivos.

AddHandler e eventos de teclado já manipulados

Você pode usar uma técnica especial para anexar manipuladores que podem agir em eventos que você já marcou 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 usa um parâmetro do tipo RoutedEvent que identifica o evento roteado em questão. Nem todos os eventos roteados fornecem um identificador RoutedEvent e, portanto, essa consideração afeta quais eventos roteados ainda podem ser tratados no caso Handled . Os eventos KeyDown e KeyUp têm identificadores de evento roteados (KeyDownEvent e KeyUpEvent) em UIElement. No entanto, outros eventos, como TextBox.TextChanged, não têm identificadores de evento roteados e, portanto, não podem ser usados com a técnica AddHandler.

Substituindo eventos e comportamento do teclado

Você pode substituir eventos de 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, subclassificamos o controle e substituímos o comportamento KeyDown para mover o foco para o conteúdo GridView quando qualquer tecla de seta é 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 estiver usando um GridView somente para layout, considere usar outros controles, como ItemsControl com ItemsWrapGrid.

Comando

Um pequeno número de elementos de interface do usuário fornece suporte interno para comandos. O comando usa eventos roteados relacionados à entrada em sua implementação subjacente. Ele permite o processamento de entrada de interface do usuário relacionada, como uma determinada ação de ponteiro ou uma tecla aceleradora específica, invocando um único manipulador de comandos.

Se o comando estiver disponível para um elemento da interface do usuário, considere usar suas APIs de comando em vez de quaisquer eventos de entrada discretos. Para obter mais informações, consulte ButtonBase.Command.

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

Entrada de texto e controles

Determinados controles reagem a eventos de teclado com seu próprio tratamento. Por exemplo, TextBox é um controle projetado para capturar e representar visualmente o texto que foi inserido usando o teclado. Ele usa KeyUp e KeyDown em sua própria lógica para capturar pressionamentos de tecla e, em seguida, também gera seu próprio evento TextChanged se o texto realmente for alterado.

Geralmente, você ainda pode adicionar manipuladores para KeyUp e KeyDown a um TextBox ou a qualquer controle relacionado destinado a processar a entrada de texto. No entanto, como parte de seu design pretendido, um controle pode não responder a todos os valores-chave direcionados a ele por meio de eventos-chave. O comportamento é específico para cada controle.

Por exemplo, ButtonBase (a classe base de Button) processa KeyUp para que ele possa verificar a tecla Barra de espaço ou Enter. O ButtonBase considera o KeyUp equivalente a um botão esquerdo do mouse para fins de gerar 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 esteja escutando um evento de tecla, no caso de uma barra de espaço, não receberia o evento já manipulado para seus próprios manipuladores.

Outro exemplo é TextBox. Algumas teclas, como as teclas de seta, 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. O TextBox marca esses casos de evento como manipulados.

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

O teclado virtual

Os controles de entrada de texto fornecem 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 fica oculto.

Quando o teclado virtual é exibido, ele reposiciona automaticamente a interface do usuário para garantir que o elemento focado permaneça visível. Isso pode fazer com que outras áreas importantes da interface do usuário saiam da tela. No entanto, você pode desativar o comportamento padrão e fazer seus próprios ajustes na interface do usuário quando o teclado virtual for exibido. Para obter mais informações, consulte o exemplo de teclado virtual.

Se você criar um controle personalizado que exija entrada de texto, mas não derive de um controle de entrada de texto padrão, poderá adicionar suporte ao teclado virtual implementando os padrões de controle corretos de Automação da Interface do Usuário. Para obter mais informações, consulte o exemplo de teclado virtual.

O pressionamento de teclas no teclado virtual aumenta os eventos KeyDown e KeyUp, assim como os pressionamentos de teclas em teclados de hardware. No entanto, o teclado virtual não gerará eventos de entrada para Ctrl+A, Ctrl+Z, Ctrl+X, Ctrl+C e Ctrl+V, que são reservados para manipulação de texto no controle de entrada.

É possível 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 fornece 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 somente para a inserção de um PIN de 4 dígitos, defina a propriedade InputScope como Number. Isso informa ao sistema para mostrar o layout do teclado numérico, o que facilita a inserção do PIN. Para obter mais detalhes, consulte Usar o escopo de entrada para alterar o teclado virtual.

Desenvolvedores

Designers

Amostras

Exemplos de arquivos