Eventos de teclado

Eventos de teclado y foco

Los siguientes eventos de teclado pueden producirse para teclados táctiles y hardware.

Evento Descripción
KeyDown Se produce cuando se presiona una tecla.
KeyUp Se produce cuando se suelta una tecla.

Importante

Algunos controles XAML controlan los eventos de entrada internamente. En estos casos, podría parecer que no se produce un evento de entrada porque el agente de escucha de eventos no invoca el controlador asociado. Normalmente, el controlador de clases procesa este subconjunto de teclas para proporcionar compatibilidad integrada con la accesibilidad básica del teclado. Por ejemplo, la clase Button invalida los eventos OnKeyDown para la tecla Space y la tecla Entrar (así como OnPointerPressed) y las enruta al evento Click del control. Cuando la clase de control controla una pulsación de teclas, no se generan los eventos KeyDown y KeyUp.
Esto proporciona un teclado integrado equivalente para invocar el botón, similar a pulsarlo con un dedo o hacer clic en él con un mouse. Las claves que no sean Space o Enter todavía activan los eventos KeyDown y KeyUp. Para obtener más información sobre cómo funciona el control basado en clases de eventos (en concreto, la sección "Controladores de eventos de entrada en controles"), consulta Información general sobre eventos y eventos enrutados.

Los controles de la interfaz de usuario generan eventos de teclado solo cuando tienen el foco de entrada. Un control individual obtiene el foco cuando el usuario hace clic o pulsa directamente en ese control en el diseño, o usa la tecla Tab para entrar en una secuencia de pestañas dentro del área de contenido.

También puede llamar al método Focus de un control para forzar el foco. Esto es necesario cuando se implementan teclas de método abreviado, ya que el foco del teclado no se establece de forma predeterminada cuando se carga la interfaz de usuario. Para obtener más información, consulta el ejemplo de teclas de método abreviado más adelante en este tema.

Para que un control reciba el foco de entrada, debe estar habilitado, visible y tener valores de propiedad IsTabStop y HitTestVisible de true. Este es el estado predeterminado para la mayoría de los controles. Cuando un control tiene el foco de entrada, puede generar y responder a eventos de entrada de teclado como se describe más adelante en este tema. También puede responder a un control que recibe o pierde el foco controlando los eventos GotFocus y LostFocus.

De forma predeterminada, la secuencia de pestañas de los controles es el orden en el que aparecen en el lenguaje de marcado extensible de aplicaciones (XAML). Sin embargo, puede modificar este orden mediante la propiedad TabIndex. Para obtener más información, consulta Implementación de la accesibilidad del teclado.

Controladores de eventos de teclado

Un controlador de eventos de entrada implementa un delegado que proporciona la siguiente información:

  • Remitente del evento. El remitente informa del objeto donde está asociado el controlador de eventos.
  • Datos de evento Para los eventos de teclado, esos datos serán una instancia de KeyRoutedEventArgs. El delegado para controladores es KeyEventHandler. Las propiedades más relevantes de KeyRoutedEventArgs para la mayoría de los escenarios de controlador son Key y, posiblemente, KeyStatus.
  • OriginalSource. Dado que los eventos de teclado son eventos enrutados, los datos del evento proporcionan OriginalSource. Si permite que los eventos se propagan deliberadamente a través de un árbol de objetos, OriginalSource es a veces el objeto de preocupación en lugar del remitente. Sin embargo, eso depende del diseño. Para obtener más información sobre cómo puede usar OriginalSource en lugar de remitente, vea la sección "Eventos enrutados de teclado" de este tema, o Información general sobre eventos y eventos enrutados.

Adjuntar un controlador de eventos de teclado

Puede adjuntar funciones de controlador de eventos de teclado para cualquier objeto que incluya el evento como miembro. Esto incluye cualquier clase derivada uiElement. En el ejemplo XAML siguiente se muestra cómo adjuntar controladores para el evento KeyUp para una cuadrícula.

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

También puede adjuntar un controlador de eventos en el código. Para obtener más información, consulta Introducción a eventos y eventos enrutados.

Definición de un controlador de eventos de teclado

En el ejemplo siguiente se muestra la definición del controlador de eventos incompleto para el controlador de eventos KeyUp que se adjuntó en el ejemplo 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
  }

Uso de KeyRoutedEventArgs

Todos los eventos de teclado usan KeyRoutedEventArgs para los datos de eventos y KeyRoutedEventArgs contiene las siguientes propiedades:

Claves virtuales

El evento KeyDown se genera si se presiona una tecla. Del mismo modo, KeyUp se genera si se libera una clave. Normalmente, escucha los eventos para procesar un valor de clave específico. Para determinar qué tecla se presiona o libera, compruebe el valor clave en los datos del evento. La clave devuelve un valor VirtualKey. La enumeración VirtualKey incluye todas las claves admitidas.

Teclas modificadoras

Las teclas modificadoras son teclas como Ctrl o Mayús que los usuarios suelen presionar en combinación con otras teclas. La aplicación puede usar estas combinaciones como métodos abreviados de teclado personalizados para invocar comandos de aplicación.

Nota:

Para obtener métodos abreviados de teclado integrados, consulte Teclas de acceso y Aceleradores de teclado.

Puede detectar combinaciones de teclas de método abreviado en los controladores de eventos KeyDown y KeyUp. Cuando se produce un evento de teclado para una tecla no modificadora, puede comprobar si una tecla modificadora está en estado presionado.

Como alternativa, la función GetKeyState() de CoreWindow (obtenida a través de CoreWindow.GetForCurrentThread()) también se puede usar para comprobar el estado modificador cuando se presiona una tecla no modificadora.

En los ejemplos siguientes se implementa este segundo método, al mismo tiempo que se incluye código auxiliar para la primera implementación.

Nota:

La tecla Alt se representa mediante el valor VirtualKey.Menu .

Ejemplo de teclas de método abreviado

En el ejemplo siguiente se muestra cómo implementar un conjunto de teclas de método abreviado personalizadas. En este ejemplo, los usuarios pueden controlar la reproducción multimedia mediante los botones Reproducir, Pausar y Detener o Ctrl+P, Ctrl+A y Ctrl+S métodos abreviados de teclado. El XAML del botón muestra los accesos directos mediante la información sobre herramientas y las propiedades AutomationProperties en las etiquetas de botón. Esta documentación automática es importante para aumentar la facilidad de uso y accesibilidad de la aplicación. Para obtener más información, consulta Accesibilidad del teclado.

Tenga en cuenta también que la página establece el foco de entrada en sí mismo cuando se carga. Sin este paso, ningún control tiene el foco de entrada inicial y la aplicación no genera eventos de entrada hasta que el usuario establece el foco de entrada manualmente (por ejemplo, mediante tabulación en o haciendo clic en un control).

<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

Nota:

Establecer AutomationProperties.AcceleratorKey o AutomationProperties.AccessKey en XAML proporciona información de cadena, que documenta la tecla de método abreviado para invocar esa acción concreta. Microsoft Automatización de la interfaz de usuario captura la información, como Narrador, y normalmente se proporciona directamente al usuario.

Establecer AutomationProperties.AcceleratorKey o AutomationProperties.AccessKey no tiene ninguna acción por sí misma. Todavía tendrás que adjuntar controladores para eventos KeyDown o KeyUp para implementar realmente el comportamiento del método abreviado de teclado en tu aplicación. Además, la decoración de texto subrayado para una tecla de acceso no se proporciona automáticamente. Debe subrayar explícitamente el texto de la clave específica en el formato de subrayado insertado si desea mostrar texto subrayado en la interfaz de usuario.

 

Eventos enrutados de teclado

Algunos eventos son eventos enrutados, incluidos KeyDown y KeyUp. Los eventos enrutados usan la estrategia de enrutamiento de propagación. La estrategia de enrutamiento de propagación significa que un evento se origina desde un objeto secundario y, a continuación, se enruta a objetos primarios sucesivos en el árbol de objetos. Esto presenta otra oportunidad para controlar el mismo evento e interactuar con los mismos datos de eventos.

Considere el ejemplo XAML siguiente, que controla los eventos KeyUp para un canvas y dos objetos Button. En este caso, si libera una clave mientras el foco se mantiene en cualquier objeto Button , genera el evento KeyUp . A continuación, el evento se propaga hasta el lienzo primario.

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

En el ejemplo siguiente se muestra cómo implementar el controlador de eventos KeyUp para el contenido XAML correspondiente en el ejemplo 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 el uso de la propiedad OriginalSource en el controlador anterior. Aquí, OriginalSource informa del objeto que generó el evento. El objeto no pudo ser StackPanel porque StackPanel no es un control y no puede tener el foco. Solo uno de los dos botones dentro del StackPanel podría haber generado el evento, pero ¿cuál? Se usa OriginalSource para distinguir el objeto de origen del evento real, si controla el evento en un objeto primario.

La propiedad Handled en datos de eventos

En función de la estrategia de control de eventos, es posible que solo desee que un controlador de eventos reaccione a un evento de propagación. Por ejemplo, si tiene un controlador KeyUp específico asociado a uno de los controles Button, tendría la primera oportunidad de controlar ese evento. En este caso, es posible que no desee que el panel primario también controle el evento. En este escenario, puede usar la propiedad Handled en los datos del evento.

El propósito de la propiedad Handled en una clase de datos de eventos enrutado es informar de que otro controlador que registró anteriormente en la ruta de eventos ya ha actuado. Esto influye en el comportamiento del sistema de eventos enrutado. Cuando se establece Handled en true en un controlador de eventos, ese evento detiene el enrutamiento y no se envía a los elementos primarios sucesivos.

Eventos de teclado AddHandler y ya controlados

Puede usar una técnica especial para adjuntar controladores que pueden actuar en eventos que ya se han marcado como controlados. Esta técnica usa el método AddHandler para registrar un controlador, en lugar de usar atributos XAML o sintaxis específica del lenguaje para agregar controladores, como += en C#.

Una limitación general de esta técnica es que la API AddHandler toma un parámetro de tipo RoutedEvent que identifica el evento enrutado en cuestión. No todos los eventos enrutados proporcionan un identificador RoutedEvent y, por tanto, esta consideración afecta a qué eventos enrutados todavía se pueden controlar en el caso controlado. Los eventos KeyDown y KeyUp tienen identificadores de eventos enrutados (KeyDownEvent y KeyUpEvent) en UIElement. Sin embargo, otros eventos como TextBox.TextChanged no tienen identificadores de eventos enrutados y, por tanto, no se pueden usar con la técnica AddHandler .

Invalidación de eventos y comportamiento del teclado

Puedes invalidar eventos clave para controles específicos (como GridView) para proporcionar una navegación de foco coherente para varios dispositivos de entrada, incluido el teclado y el controlador para juegos.

En el ejemplo siguiente, subclase el control e invalidamos el comportamiento de KeyDown para mover el foco al contenido de GridView cuando se presiona cualquier tecla de flecha.

  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);
    }
  }

Nota:

Si usa gridView solo para el diseño, considere la posibilidad de usar otros controles como ItemsControl con ItemsWrapGrid.

Comandos

Un pequeño número de elementos de la interfaz de usuario proporcionan compatibilidad integrada con el comando. El comando usa eventos enrutados relacionados con la entrada en su implementación subyacente. Permite el procesamiento de la entrada de interfaz de usuario relacionada, como una determinada acción de puntero o una tecla de aceleración específica, invocando un único controlador de comandos.

Si el comando está disponible para un elemento de interfaz de usuario, considera la posibilidad de usar sus API de comandos en lugar de cualquier evento de entrada discreto. Para obtener más información, consulta ButtonBase.Command.

También puede implementar ICommand para encapsular la funcionalidad de comandos que se invoca desde controladores de eventos normales. Esto le permite usar comandos incluso cuando no hay ninguna propiedad Command disponible.

Entrada de texto y controles

Algunos controles reaccionan a los eventos de teclado con su propio control. Por ejemplo, TextBox es un control diseñado para capturar y, a continuación, representar visualmente el texto que se especificó mediante el teclado. Usa KeyUp y KeyDown en su propia lógica para capturar pulsaciones de tecla y, a continuación, también genera su propio evento TextChanged si el texto ha cambiado realmente.

Por lo general, puede agregar controladores para KeyUp y KeyDown a un TextBox, o cualquier control relacionado destinado a procesar la entrada de texto. Sin embargo, como parte de su diseño previsto, es posible que un control no responda a todos los valores de clave que se le dirigen a través de eventos clave. El comportamiento es específico de cada control.

Por ejemplo, ButtonBase (la clase base para Button) procesa KeyUp para que pueda comprobar la barra espaciadora o la tecla Entrar. ButtonBase considera keyUp equivalente a un botón izquierdo del mouse para generar un evento Click. Este procesamiento del evento se realiza cuando ButtonBase invalida el método virtual OnKeyUp. En su implementación, establece Handled en true. El resultado es que cualquier elemento primario de un botón que escucha un evento clave, en el caso de una barra espaciadora, no recibiría el evento ya controlado para sus propios controladores.

Otro ejemplo es TextBox. Algunas teclas, como las teclas de dirección, no se consideran texto por TextBox y, en su lugar, se consideran específicas del comportamiento de la interfaz de usuario de control. TextBox marca estos casos de evento como controlado.

Los controles personalizados pueden implementar su propio comportamiento de invalidación similar para eventos clave invalidando OnKeyDown / OnKeyUp. Si el control personalizado procesa teclas de aceleración específicas o tiene un comportamiento de control o enfoque similar al escenario descrito para TextBox, debe colocar esta lógica en sus propias invalidaciones OnKeyDown / OnKeyUp.

Teclado táctil

Los controles de entrada de texto proporcionan compatibilidad automática con el teclado táctil. Cuando el usuario establece el foco de entrada en un control de texto mediante la entrada táctil, el teclado táctil aparece automáticamente. Cuando el foco de entrada no está en un control de texto, el teclado táctil está oculto.

Cuando aparezca el teclado táctil, cambia automáticamente la posición de la interfaz de usuario para asegurarse de que el elemento centrado permanece visible. Esto puede hacer que otras áreas importantes de la interfaz de usuario se muevan fuera de la pantalla. Sin embargo, puede deshabilitar el comportamiento predeterminado y realizar sus propios ajustes de interfaz de usuario cuando aparezca el teclado táctil. Para obtener más información, consulta el ejemplo de teclado táctil.

Si crea un control personalizado que requiere entrada de texto, pero no deriva de un control de entrada de texto estándar, puede agregar compatibilidad con el teclado táctil implementando los patrones de control de Automatización de la interfaz de usuario correctos. Para obtener más información, consulta el ejemplo de teclado táctil.

Pulsa teclas en el teclado táctil eleva los eventos KeyDown y KeyUp, al igual que las pulsaciones de tecla en los teclados de hardware. Sin embargo, el teclado táctil no generará eventos de entrada para Ctrl+A, Ctrl+Z, Ctrl+X, Ctrl+C y Ctrl+V, que están reservados para la manipulación de texto en el control de entrada.

Para que los usuarios puedan escribir datos en la aplicación de forma mucho más rápida y sencilla, establezca el ámbito de entrada del control de texto para que coincida con el tipo de datos que espera que introduzca el usuario. El ámbito de entrada proporciona una sugerencia sobre el tipo de entrada de texto esperado por el control para que el sistema pueda proporcionar un diseño de teclado táctil especializado para el tipo de entrada. Por ejemplo, si un cuadro de texto se usa únicamente para escribir un PIN de 4 dígitos, establece la propiedad InputScope en Number. Esto indica al sistema que muestre el diseño del teclado numérico, lo que facilita que el usuario escriba el PIN. Para obtener más información, consulte Usar el ámbito de entrada para cambiar el teclado táctil.

Desarrolladores

Diseñadores

Ejemplos

Ejemplos de archivo