Información general sobre acciones del usuario

El subsistema de Windows Presentation Foundation (WPF) proporciona una API eficaz para obtener datos desde una variedad de dispositivos, como el mouse, el teclado, las funciones táctiles y el lápiz. En este tema se describen los servicios que proporciona WPF y se explica la arquitectura de los sistemas de entrada.

API de entrada

La exposición de la API de entrada principal se encuentra en las clases de elemento base: UIElement, ContentElement, FrameworkElement y FrameworkContentElement. Para obtener más información sobre los elementos base, consulte Información general sobre los elementos base. Estas clases proporcionan funcionalidad para los eventos de entrada relacionados con las pulsaciones de teclas, los botones del mouse, la rueda del mouse, el movimiento del mouse, la administración del foco, la captura del mouse, etc. Al situar la API de entrada en los elementos base, en lugar de tratar todos los eventos de entrada como un servicio, la arquitectura de entrada permite que los eventos de entrada tengan su origen en un objeto determinado de la interfaz de usuario y que admitan un esquema de enrutamiento de eventos en que más de un elemento tiene la oportunidad de controlar un evento de entrada. Muchos eventos de entrada tienen un par de eventos asociados. Por ejemplo, el evento soltar tecla está asociado con los eventos KeyDown y PreviewKeyDown. La diferencia entre estos eventos radica en cómo se redirigen al elemento de destino. Los eventos de vista previa tunelizan el árbol de elementos desde el elemento raíz al de destino. Los eventos de propagación se propagan del elemento de destino al elemento raíz. El enrutamiento de eventos en WPF se describe con más detalle más adelante, dentro de esta información general, y en Información general sobre eventos enrutados.

Clases Keyboard y Mouse

Además de la API de entrada de las clases de elementos base, las clases Keyboard y Mouse proporcionan API adicionales para trabajar con entradas de teclado y mouse.

Algunos ejemplos de la API de entrada de la clase Keyboard son la propiedad Modifiers, que devuelve el objeto ModifierKeys presionado actualmente y el método IsKeyDown, que determina si se presiona una tecla especificada.

El siguiente ejemplo utiliza el método GetKeyStates para determinar si un objeto Key está en estado presionado.

// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison.
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
    btnNone.Background = Brushes.Red;
}
' Uses the Keyboard.GetKeyStates to determine if a key is down.
' A bitwise AND operation is used in the comparison. 
' e is an instance of KeyEventArgs.
If (Keyboard.GetKeyStates(Key.Return) And KeyStates.Down) > 0 Then
    btnNone.Background = Brushes.Red

Algunos ejemplos de API de entrada en la clase Mouse son MiddleButton, que obtiene el estado del botón central del mouse, y DirectlyOver, que obtiene el elemento sobre el que está el puntero del mouse actualmente.

En el siguiente ejemplo, se determina si el objeto LeftButton del mouse está en estado Pressed.

if (Mouse.LeftButton == MouseButtonState.Pressed)
{
    UpdateSampleResults("Left Button Pressed");
}
If Mouse.LeftButton = MouseButtonState.Pressed Then
    UpdateSampleResults("Left Button Pressed")
End If

Las clases Mouse y Keyboard se tratan con más detalle en esta información general.

Entrada del lápiz

WPF tiene compatibilidad integrada con Stylus. Stylus es una entrada de lápiz que se popularizó con el Tablet PC. Las aplicaciones de WPF pueden tratar el lápiz como un mouse mediante la API de mouse, pero WPF también cuenta con una abstracción del dispositivo de lápiz que usa un modelo similar al del teclado y el mouse. Todas las API relacionadas con el lápiz contienen la palabra "Stylus".

Dado que el lápiz puede actuar como un mouse, las aplicaciones que solo admiten la entrada de mouse pueden tener cierto nivel de compatibilidad con el lápiz automáticamente. Cuando se usa el lápiz de esta forma, la aplicación tiene la oportunidad de controlar el evento de lápiz adecuado y, a continuación, el evento de mouse correspondiente. Además, los servicios de nivel superior, como las entradas manuscritas, también están disponibles a través de la abstracción del dispositivo de lápiz. Para obtener más información sobre la escritura con lápiz como entrada, consulte Introducción a las entradas manuscritas.

Enrutamiento de eventos

FrameworkElement puede contener otros elementos, como elementos secundarios en su modelo de contenido, que forman un árbol de elementos. En WPF, el elemento principal puede participar en la entrada dirigida a sus elementos secundarios o a otros descendientes al controlar los eventos. Esto es especialmente útil para construir controles a partir de otros más pequeños, un proceso conocido como "composición de control" o "composición". Para más información sobre los árboles de elementos y cómo se relacionan con las rutas de eventos, consulte Árboles en WPF.

El enrutamiento de eventos es el proceso de reenviar eventos a varios elementos para que un objeto o elemento determinado de la ruta pueda elegir ofrecer una respuesta significativa (a través del control) a un evento que podría tener su origen en otro elemento. Los eventos enrutados usan uno de los tres mecanismos de enrutamiento: directo, propagación y tunelización. En el enrutamiento directo, el elemento de origen es el único elemento notificado y el evento no se redirige a ningún otro elemento. Sin embargo, el evento enrutado directo todavía ofrece algunas funcionalidades adicionales que solo están presentes para eventos enrutados, en lugar de para eventos de CLR estándar. La propagación prepara el árbol de elementos: en primer lugar, notifica al elemento que dio origen al evento; a continuación, al elemento primario, etc. La tunelización comienza en la raíz del árbol de elementos y desciende para acabar con el elemento de origen original. Para obtener más información sobre los eventos enrutados, vea Información general sobre eventos enrutados.

Los eventos de entrada de WPF suelen encontrarse en parejas formadas por un evento de tunelización y otro de propagación. Los eventos de tunelización se distinguen de los eventos de propagación con el prefijo "Preview". Por ejemplo, PreviewMouseMove es la versión de tunelización de un evento de movimiento del mouse y MouseMove es la versión de propagación del mismo evento. Este emparejamiento de eventos es una convención que se implementa en el nivel de elemento y no es una funcionalidad propia del sistema de eventos de WPF. Para obtener más información, consulte la sección Eventos de entrada de WPF en Información general sobre eventos enrutados.

Control de eventos de entrada

Para recibir una entrada en un elemento, se debe asociar un controlador de eventos con el evento concreto. En XAML es sencillo: se debe hacer referencia al nombre del evento como atributo del elemento que se escuchará para este evento. A continuación, se establece el valor del atributo con el nombre del controlador de eventos que se define, en función de un delegado. El controlador de eventos debe escribirse en código, como C#, y puede incluirse en un archivo de código subyacente.

Los eventos de teclado tienen lugar cuando el sistema operativo notifica acciones de teclas que se producen mientras el foco del teclado está en un elemento. Los eventos de mouse y lápiz se dividen en dos categorías: eventos que informan de cambios en la posición del puntero en relación con el elemento y eventos que informan de cambios en el estado de los botones del dispositivo.

Ejemplo de evento de entrada de teclado

En el ejemplo siguiente se escucha una pulsación de tecla de flecha izquierda. Se crea un objeto StackPanel que tiene un elemento Button. Se asocia a la instancia de Button un controlador de eventos para escuchar la pulsación de la tecla de dirección izquierda.

En la primera sección del ejemplo se crea el elemento StackPanel y el elemento Button y se asocia el controlador de eventos para KeyDown.

<StackPanel>
  <Button Background="AliceBlue"
          KeyDown="OnButtonKeyDown"
          Content="Button1"/>
</StackPanel>
// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();

// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";

// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);

// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);
' Create the UI elements.
Dim keyboardStackPanel As New StackPanel()
Dim keyboardButton1 As New Button()

' Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue
keyboardButton1.Content = "Button 1"

' Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1)

' Attach event handler.
AddHandler keyboardButton1.KeyDown, AddressOf OnButtonKeyDown

La segunda sección está escrita en código y define el controlador de eventos. Cuando se presiona la tecla de dirección izquierda y el elemento Button tiene el foco del teclado, se ejecuta el controlador y el color de Background del elemento Button cambia. Si se pulsa la tecla, pero no es la tecla de dirección izquierda, el color de Background del elemento Button vuelve a cambiar al color inicial.

private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
    Button source = e.Source as Button;
    if (source != null)
    {
        if (e.Key == Key.Left)
        {
            source.Background = Brushes.LemonChiffon;
        }
        else
        {
            source.Background = Brushes.AliceBlue;
        }
    }
}
Private Sub OnButtonKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    Dim source As Button = TryCast(e.Source, Button)
    If source IsNot Nothing Then
        If e.Key = Key.Left Then
            source.Background = Brushes.LemonChiffon
        Else
            source.Background = Brushes.AliceBlue
        End If
    End If
End Sub

Ejemplo de evento de entrada de mouse

En el ejemplo siguiente, el color de Background de un elemento Button cambia cuando el puntero del mouse entra en el elemento Button. El color de Background se restaura cuando el mouse sale del elemento Button.

En la primera sección del ejemplo se crea el elemento StackPanel y el control Button y se asocian los controladores de eventos de los eventos MouseEnter y MouseLeave al elemento Button.

<StackPanel>
  <Button Background="AliceBlue"
          MouseEnter="OnMouseExampleMouseEnter"
          MouseLeave="OnMosueExampleMouseLeave">Button
          
  </Button>
</StackPanel>
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();

// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";

// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);

// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);
' Create the UI elements.
Dim mouseMoveStackPanel As New StackPanel()
Dim mouseMoveButton As New Button()

' Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue
mouseMoveButton.Content = "Button"

' Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton)

' Attach event handler.
AddHandler mouseMoveButton.MouseEnter, AddressOf OnMouseExampleMouseEnter
AddHandler mouseMoveButton.MouseLeave, AddressOf OnMosueExampleMouseLeave

La segunda sección del ejemplo está escrita en código y define los controladores de eventos. Cuando el mouse entra en el elemento Button, el color de Background del elemento Button cambia a SlateGray. Cuando el mouse sale del elemento Button, el color de Background del elemento Button cambia de nuevo a AliceBlue.

private void OnMouseExampleMouseEnter(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.SlateGray;
    }
}
Private Sub OnMouseExampleMouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs)
    ' Cast the source of the event to a Button.
    Dim source As Button = TryCast(e.Source, Button)

    ' If source is a Button.
    If source IsNot Nothing Then
        source.Background = Brushes.SlateGray
    End If
End Sub
private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.AliceBlue;
    }
}
Private Sub OnMosueExampleMouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs)
    ' Cast the source of the event to a Button.
    Dim source As Button = TryCast(e.Source, Button)

    ' If source is a Button.
    If source IsNot Nothing Then
        source.Background = Brushes.AliceBlue
    End If
End Sub

Entrada de texto

El evento TextInput le permite escuchar la entrada de texto de forma independiente del dispositivo. El teclado es el medio principal de entrada de texto, pero la voz, la escritura a mano y otros dispositivos de entrada también pueden generar entradas de texto.

Para la entrada del teclado, WPF envía primero los eventos KeyDown/KeyUp adecuados. Si dichos eventos no se controlan y la tecla es textual (en lugar de una tecla de control, como una flecha direccional o una tecla de función), se genera un evento TextInput. No siempre hay una asignación unívoca simple entre los eventos KeyDown/KeyUp y TextInput, ya que pulsaciones de teclas múltiples pueden generar un único carácter de entrada de texto y algunas pulsaciones de teclas únicas pueden generar cadenas de varios caracteres. Esto es especialmente cierto en el caso de idiomas como el chino, japonés y coreano, que usan Editores de métodos de entrada (IME) para generar los miles de caracteres posibles de sus alfabetos correspondientes.

Cuando WPF envía un evento KeyUp/KeyDown, Key se establece en Key.System si las pulsaciones de teclas pueden convertirse en parte de un evento TextInput (si se presiona ALT+S, por ejemplo). Esto permite que el código de un controlador de eventos KeyDown busque Key.System y, si lo encuentra, deje de procesar el controlador del evento TextInput generado posteriormente. En estos casos, las distintas propiedades del argumento TextCompositionEventArgs se pueden usar para determinar las pulsaciones de teclas originales. Igualmente, si un IME está activo, Key tiene el valor de Key.ImeProcessed, y ImeProcessedKey proporciona la pulsación o las pulsaciones de teclas originales.

En el ejemplo siguiente se define un controlador para el evento Click y un controlador para el evento KeyDown.

El primer segmento de código o marcado crea la interfaz de usuario.

<StackPanel KeyDown="OnTextInputKeyDown">
  <Button Click="OnTextInputButtonClick"
          Content="Open" />
  <TextBox> . . . </TextBox>
</StackPanel>
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";

// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);

// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);
' Create the UI elements.
Dim textInputStackPanel As New StackPanel()
Dim textInputeButton As New Button()
Dim textInputTextBox As New TextBox()
textInputeButton.Content = "Open"

' Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton)
textInputStackPanel.Children.Add(textInputTextBox)

' Attach event handlers.
AddHandler textInputStackPanel.KeyDown, AddressOf OnTextInputKeyDown
AddHandler textInputeButton.Click, AddressOf OnTextInputButtonClick

El segundo segmento de código contiene los controladores de eventos.

private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
    {
        handle();
        e.Handled = true;
    }
}

private void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
    handle();
    e.Handled = true;
}

public void handle()
{
    MessageBox.Show("Pretend this opens a file");
}
Private Sub OnTextInputKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    If e.Key = Key.O AndAlso Keyboard.Modifiers = ModifierKeys.Control Then
        handle()
        e.Handled = True
    End If
End Sub

Private Sub OnTextInputButtonClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
    handle()
    e.Handled = True
End Sub

Public Sub handle()
    MessageBox.Show("Pretend this opens a file")
End Sub

Dado que los eventos de entrada propagan la ruta del evento, el elemento StackPanel recibe la entrada, independientemente del elemento que tenga el foco del teclado. Primero se notifica el control TextBox y se llama al controlador OnTextInputKeyDown solo si el elemento TextBox no controló la entrada. Si se usa el evento PreviewKeyDown en lugar del evento KeyDown, se llama primero al controlador OnTextInputKeyDown.

En este ejemplo, la lógica de control se escribe dos veces: una vez para CTRL+O y otra para el evento de clic del botón. Esto se puede simplificar mediante el uso de comandos, en lugar de controlar los eventos de entrada directamente. Los comandos se describen en esta información general y en Información general sobre comandos.

Funciones táctiles y manipulación

El nuevo hardware y la API en el sistema operativo Windows 7 proporcionan a las aplicaciones la capacidad de recibir entradas de varios toques simultáneamente. WPF permite que las aplicaciones detecten y respondan a una función táctil de manera similar a como responden a otras entradas, como el mouse o el teclado, al generar eventos cuando se produce el toque.

WPF expone dos tipos de eventos cuando se produce el toque: eventos de función táctil y eventos de manipulación. Los eventos de función táctil proporcionan datos sin procesar sobre cada dedo en una pantalla táctil y su movimiento. Los eventos de manipulación interpretan la entrada como determinadas acciones. Ambos tipos de eventos se describen en esta sección.

Prerrequisitos

Para desarrollar una aplicación que responda a las funciones táctiles, necesitará los componentes siguientes.

  • Visual Studio 2010.

  • Windows 7.

  • Un dispositivo, como una pantalla táctil, compatible con Windows Touch.

Terminología

Para referirse a la función táctil, se usan los términos siguientes.

  • La función táctil es un tipo de entrada de usuario que reconoce Windows 7. Normalmente, se inicia colocando los dedos en una pantalla táctil. Tenga en cuenta que algunos dispositivos, como el panel táctil habitual en portátiles, no admiten la función táctil cuando se limitan a convertir la posición y el movimiento del dedo a entradas de mouse.

  • La función multitáctil es una función táctil que tiene lugar desde más de un punto de forma simultánea. Windows 7 y WPF admiten la función multitáctil. Cada vez que se menciona la función táctil en la documentación de WPF, los conceptos se aplican a la función multitáctil.

  • Una manipulación se produce cuando la función táctil se interpreta como una acción física que se aplica a un objeto. En WPF, los eventos de manipulación interpretan las entradas como una manipulación de traducción, expansión o rotación.

  • touch device representa un dispositivo que genera entradas táctiles, como un solo dedo en una pantalla táctil.

Controles que responden a la función táctil

Los siguientes controles se pueden desplazar al arrastrar un dedo por el control si tiene contenido que se desplaza fuera de la vista.

El objeto ScrollViewer define la propiedad asociada ScrollViewer.PanningMode que le permite especificar si el planeamiento de las entradas táctiles está habilitado de forma horizontal, vertical, de las dos formas o de ninguna. La propiedad ScrollViewer.PanningDeceleration especifica cómo de rápido se ralentiza el desplazamiento cuando el usuario levanta el dedo de la pantalla táctil. La propiedad asociada ScrollViewer.PanningRatio especifica la proporción de desplazamiento en pantalla para traducir el desplazamiento de manipulación.

Eventos de funciones táctiles

Las clases base, UIElement, UIElement3D y ContentElement, definen los eventos a los que se puede suscribir para que la aplicación responda a entradas táctiles. Los eventos de funciones táctiles resultan útiles cuando la aplicación interpreta la función táctil como algo distinto a la manipulación de un objeto. Por ejemplo, una aplicación que permite a un usuario dibujar con uno o más dedos podría suscribirse a los eventos de función táctil.

Las tres clases definen los siguientes eventos, que se comportan de forma similar, independientemente de la clase de definición.

Del mismo modo que los eventos de teclado y mouse, los eventos de función táctil son eventos enrutados. Los eventos que empiezan por Preview son eventos de tunelización, mientras que los que empiezan por Touch son eventos de propagación. Para obtener más información sobre los eventos enrutados, vea Información general sobre eventos enrutados. Al controlar estos eventos, puede obtener la posición de la entrada, relativa a cualquier elemento, mediante la llamada al método GetTouchPoint o GetIntermediateTouchPoints.

Para entender la interacción entre los eventos de función táctil, considere un escenario en que un usuario coloca un dedo en un elemento, lo mueve en el elemento y, a continuación, lo levanta. En la ilustración siguiente se muestra la ejecución de los eventos de propagación (los eventos de tunelización se omiten por motivos de simplicidad).

La secuencia de eventos de función táctil. Eventos de función táctil

En la lista siguiente se describe la secuencia de los eventos de la ilustración anterior.

  1. El evento TouchEnter tiene lugar una vez cuando el usuario pone un dedo sobre el elemento.

  2. El evento TouchDown se produce una vez.

  3. El evento TouchMove tiene lugar varias veces a medida que el usuario mueve el dedo dentro del elemento.

  4. El evento TouchUp tiene lugar una vez cuando el usuario levanta el dedo del elemento.

  5. El evento TouchLeave se produce una vez.

Cuando se usan más de dos dedos, se producen eventos para cada dedo.

Eventos de manipulación

Para los casos en que una aplicación permite a un usuario manipular un objeto, la clase UIElement define eventos de manipulación. A diferencia de los eventos de función táctil que, simplemente, notifican la posición de la función táctil, los eventos de manipulación indican cómo se puede interpretar la entrada. Hay tres tipos de manipulación: traducción, expansión y rotación. En la lista siguiente se describe cómo invocar los tres tipos de manipulaciones.

  • Coloque un dedo en un objeto y mueva el dedo por la pantalla táctil para invocar una manipulación de traducción. Normalmente, esto mueve el objeto.

  • Coloque dos dedos en un objeto y acérquelos o aléjelos para invocar una manipulación de expansión. Normalmente, esto cambia el tamaño del objeto.

  • Coloque dos dedos en un objeto y gírelos entre sí para invocar una manipulación de rotación. Normalmente, esto gira el objeto.

Se puede producir más de un tipo de manipulación al mismo tiempo.

Cuando hace que los objetos respondan a manipulaciones, puede hacer que parezca que el objeto tenga inercia. Esto puede hacer que los objetos simulen el mundo físico. Por ejemplo, si empuja un libro encima de una mesa con suficiente fuerza, el libro seguirá moviéndose cuando lo suelte. WPF le permite simular este comportamiento, ya que puede generar eventos de manipulación cuando los dedos del usuario liberan el objeto.

Para obtener información sobre cómo crear una aplicación que permita al usuario mover y girar un objeto, o cambiar su tamaño, consulte Tutorial: Crear su primera aplicación táctil.

UIElement define los eventos de manipulación siguientes.

De forma predeterminada, un elemento UIElement no recibe estos eventos de manipulación. Para recibir eventos de manipulación en un elemento UIElement, establezca UIElement.IsManipulationEnabled en true.

Ruta de acceso de ejecución de eventos de manipulación

Considere un escenario en que un usuario "lanza" un objeto. El usuario coloca un dedo en el objeto, mueve el dedo por la pantalla táctil una distancia corta y, a continuación, lo levanta mientras se mueve. El resultado es que el objeto se moverá bajo el dedo del usuario y lo seguirá haciendo cuando el usuario lo levante.

En la ilustración siguiente se muestra la ruta de acceso de ejecución de los eventos de manipulación e información importante sobre cada evento.

La secuencia de eventos de manipulación. Eventos de manipulación

En la lista siguiente se describe la secuencia de los eventos de la ilustración anterior.

  1. El evento ManipulationStarting tiene lugar una vez cuando el usuario pone un dedo sobre el objeto. Entre otras cosas, este evento permite establecer la propiedad ManipulationContainer. En los eventos posteriores, la posición de la manipulación será relativa a ManipulationContainer. En eventos distintos de ManipulationStarting, esta propiedad es de solo lectura, por lo que solo se puede definir esta propiedad para el evento ManipulationStarting.

  2. El evento ManipulationStarted se produce a continuación. Este evento indica el origen de la manipulación.

  3. El evento ManipulationDelta tiene lugar varias veces a medida que el usuario mueve los dedos en una pantalla táctil. La propiedad DeltaManipulation de la clase ManipulationDeltaEventArgs indica si la manipulación se interpreta como movimiento, expansión o traducción. En este punto se realiza la mayoría del trabajo de manipulación de un objeto.

  4. El evento ManipulationInertiaStarting tiene lugar cuando los dedos del usuario pierden contacto con el objeto. Este evento permite especificar la desaceleración de las manipulaciones durante la inercia. Esto sirve para que el objeto pueda emular espacios físicos o atributos diferentes, si así lo elige. Por ejemplo, suponga que la aplicación tiene dos objetos que representan elementos del mundo físico y que uno es más pesado que el otro. Puede hacer que el objeto más pesado disminuya su velocidad más rápidamente que el objeto ligero.

  5. El evento ManipulationDelta se genera varias veces cuando se produce la inercia. Tenga en cuenta que este evento se produce cuando los dedos del usuario se mueven por la pantalla táctil y cuando WPF simula la inercia. En otras palabras, ManipulationDelta se produce antes y después del evento ManipulationInertiaStarting. La propiedad ManipulationDeltaEventArgs.IsInertial notifica si el evento ManipulationDelta se produce durante la inercia, para que pueda comprobar la propiedad y realizar distintas acciones, en función de su valor.

  6. El evento ManipulationCompleted se produce cuando finalizan la manipulación y la inercia. Es decir, después de que se producen todos los eventos ManipulationDelta, tiene lugar el evento ManipulationCompleted para indicar que se completó la manipulación.

El elemento UIElement también define el evento ManipulationBoundaryFeedback. Este evento se produce cuando se llama al método ReportBoundaryFeedback en el evento ManipulationDelta. El evento ManipulationBoundaryFeedbackpermite que las aplicaciones o componentes proporcionen comentarios visuales cuando un objeto alcanza un límite. Por ejemplo, la clase Window controla el evento ManipulationBoundaryFeedback para hacer que la ventana se mueva ligeramente al llegar al borde.

Puede cancelar la manipulación llamando al método Cancel en los argumentos de evento de cualquier evento de manipulación, excepto el evento ManipulationBoundaryFeedback. Al llamar a Cancel, ya no se generan eventos de manipulación y las funciones táctiles generan eventos de mouse. En la tabla siguiente se describe la relación entre el momento en que se cancela la manipulación y los eventos del mouse que se producen.

Evento con el que se llama al método Cancel Eventos del mouse que se generan para entradas que ya se han producido
ManipulationStarting y ManipulationStarted Eventos de presión del mouse.
ManipulationDelta Eventos de presión y movimiento del mouse.
ManipulationInertiaStarting y ManipulationCompleted Eventos de presión, movimiento y liberación del mouse.

Tenga en cuenta que si llama a Cancel cuando la manipulación tiene la inercia aplicada, el método devuelve false y la entrada no genera eventos de mouse.

Relación entre eventos de función táctil y manipulación

Un elemento UIElement siempre puede recibir eventos de función táctil. Cuando la propiedad IsManipulationEnabled se establece en true, un elemento UIElement puede recibir eventos de función táctil y eventos de manipulación. Si el evento TouchDown no se controla (es decir, la propiedad Handled es false), la lógica de manipulación captura la función táctil sobre el elemento y genera los eventos de manipulación. Si la propiedad Handled se establece en true en el evento TouchDown, la lógica de manipulación no genera eventos de manipulación. En la siguiente ilustración se muestra la relación entre los eventos de función táctil y los de manipulación.

Relación entre eventos de función táctil y de manipulación Eventos de función táctil y de manipulación

En la lista siguiente se describe la relación entre los eventos de función táctil y de manipulación que se muestra en la ilustración anterior.

Foco

Hay dos conceptos principales relacionados con el foco en WPF: el foco de teclado y el foco lógico.

Foco de teclado

El foco de teclado hace referencia al elemento que recibe la entrada del teclado. Puede haber un único elemento en todo el escritorio que tenga el foco de teclado. En WPF, el elemento que tiene el foco de teclado tendrá IsKeyboardFocused establecido en true. El método Keyboard estático FocusedElement devuelve el elemento que actualmente tiene el foco del teclado.

El foco del teclado se puede obtener desplazándose con el tabulador a un elemento o haciendo clic con el mouse en determinados elementos, como TextBox. El foco de teclado también se puede obtener mediante programación utilizando el método Focus de la clase Keyboard. Focus intenta dar el foco de teclado al elemento especificado. El elemento devuelto por Focus es el elemento que actualmente tiene el foco del teclado.

Para que un elemento obtenga el foco del teclado, las propiedades Focusable y IsVisible deben estar establecidas en true. Algunas clases, como Panel, tienen la propiedad Focusable establecida de forma predeterminada en false; por lo tanto, puede que deba establecer esta propiedad en true si quiere que dicho elemento pueda obtener el foco del teclado.

En el ejemplo siguiente se usa Focus para establecer el foco del teclado en un elemento Button. El lugar recomendado para establecer el foco inicial en una aplicación es en el controlador de eventos Loaded.

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
    ' Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton)
End Sub

Para obtener más información acerca del foco de teclado, consulte Foco de teclado.

Foco lógico

El foco lógico se refiere al FocusManager.FocusedElement en un ámbito de foco. Puede haber varios elementos que tengan el foco lógico en una aplicación, pero solo puede haber uno con el foco lógico en un ámbito de foco concreto.

Un ámbito de foco es un elemento contenedor que mantiene el seguimiento del elemento FocusedElement dentro de su ámbito. Cuando el foco sale de un ámbito de foco, el elemento enfocado pierde el foco de teclado, pero conserva el foco lógico. Cuando el foco vuelve al ámbito de foco, el elemento enfocado recibe el foco de teclado. Esto permite cambiar el foco de teclado entre varios ámbitos de foco, pero garantiza que el elemento enfocado dentro del ámbito siga siendo el elemento enfocado cuando vuelva el foco.

Un elemento puede convertirse en un ámbito de foco en el lenguaje XAML estableciendo la propiedad asociada FocusManagerIsFocusScope en true, o en el código estableciendo la propiedad asociada con el método SetIsFocusScope.

El siguiente ejemplo convierte un StackPanel en un ámbito de enfoque estableciendo la propiedad adjunta IsFocusScope.

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)

Las clases de WPF que son ámbitos de foco por defecto son Window, Menu, ToolBar y ContextMenu.

Un elemento que tenga el foco del teclado también tendrá el foco lógico del ámbito de foco al que pertenece. Por lo tanto, al definir el foco en un elemento con el método Focus en la clase Keyboard o las clases de elementos base, se intentará dar al elemento el foco de teclado y el foco lógico.

Para determinar el elemento que tiene el foco en un ámbito de foco, use GetFocusedElement. Para cambiar el elemento que tiene el foco en un ámbito de foco, use SetFocusedElement.

Para obtener más información acerca del foco lógico, consulte Foco lógico.

Posición del mouse

La API de entrada de WPF proporciona información útil con respecto a los espacios de coordenadas. Por ejemplo, la coordenada (0,0) es la coordenada superior izquierda, pero ¿de qué elemento del árbol? ¿El elemento de destino de la entrada? ¿El elemento adjunto al controlador de eventos? ¿O alguna otra cosa? Para evitar confusiones, la API de entrada de WPF requiere que especifique el marco de referencia cuando trabaje con coordenadas obtenidas mediante el mouse. El método GetPosition devuelve la coordenada del punto del mouse con relación al elemento especificado.

Captura del mouse

Los dispositivos de mouse tienen una característica modal concreta que se denomina captura del mouse. La captura del mouse se usa para mantener un estado de entrada transitorio cuando se inicia una operación de arrastrar y colocar, para que otras operaciones relacionadas con la posición en pantalla nominal del puntero del mouse no se produzcan necesariamente. Al arrastrar, el usuario no puede hacer clic sin anular la acción de arrastrar y colocar, lo que hace que la mayoría de las indicaciones que se producen al pasar el mouse sean inadecuadas mientras se conserva la captura del mouse desde el origen de la acción de arrastrar. El sistema de entrada expone API que pueden determinar el estado de captura del mouse, además de otras API que pueden forzar la captura del mouse en un elemento concreto o borrar el estado de la captura del mouse. Para obtener más información sobre las operaciones de arrastrar y colocar, consulte Información general sobre la función de arrastrar y colocar.

Comandos:

Los comandos habilitan el control de entrada en un nivel más semántico que la entrada de dispositivos. Los comandos son directivas sencillas, como Cut, Copy, Paste o Open. Los comandos son útiles para centralizar la lógica de comando. Se puede acceder al mismo comando desde un elemento Menu, en un elemento ToolBar, o mediante un método abreviado de teclado. Los comandos también proporcionan un mecanismo para deshabilitar controles cuando el comando deja de estar disponible.

RoutedCommand es la implementación de WPF de ICommand. Cuando se ejecuta un evento RoutedCommand, se genera un evento PreviewExecuted y un evento Executed en el destino del comando, que se tunelizan y propagan mediante el árbol de elementos como otra entrada. Si no se define ningún destino de comando, el elemento con el foco de teclado será el destino del comando. La lógica que ejecuta el comando se asocia a un evento CommandBinding. Cuando un evento Executed alcanza un evento CommandBinding para ese comando específico, se llama al evento ExecutedRoutedEventHandler en el evento CommandBinding. Este controlador realiza la acción del comando.

Para obtener más información sobre los comandos, consulte Información general sobre los comandos.

WPF proporciona una biblioteca de comandos comunes que consta de ApplicationCommands, MediaCommands, ComponentCommands, NavigationCommands y EditingCommands, o puede definir los suyos propios.

En el ejemplo siguiente se muestra cómo configurar un elemento MenuItem para que al hacer clic en él se invoque el comando Paste en el elemento TextBox, suponiendo que el elemento TextBox tenga el foco del teclado.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

Para más información sobre los comandos de WPF, consulte Información general sobre comandos.

Sistema de entrada y elementos base

El sistema de entrada genera los eventos de entrada, como los eventos adjuntos que definen las clases Mouse, Keyboard y Stylus. A continuación, se insertan en una posición concreta del modelo de objetos en función de las pruebas de posicionamiento del árbol visual en tiempo de ejecución.

Cada uno de los eventos que Mouse, Keyboard y Stylus definen como un evento adjunto también se vuelven a exponer mediante las clases UIElement de elemento base y ContentElement como nuevo evento enrutado. Los eventos enrutados de elementos base se generan desde clases que controlan el evento adjunto original y reutilizan los datos del evento.

Cuando el evento de entrada se asocia con un elemento de origen determinado a través de la implementación del evento de entrada de elemento base, se puede redirigir a través del resto de una ruta de evento que se basa en una combinación de objetos del árbol lógico y visual, y se puede controlar desde el código de la aplicación. Por lo general, es preferible controlar estos eventos de entrada relacionados con dispositivos mediante los eventos enrutados en UIElement y ContentElement, ya que puede usar una sintaxis de controlador de eventos más intuitiva en XAML y en el código. También puede elegir controlar el evento adjunto que inició el proceso, pero tendría que enfrentarse a varios problemas: el evento adjunto puede estar marcado como controlado por el control de la clase de elemento base, y debe utilizar métodos de descriptor de acceso, en lugar de la sintaxis de eventos true a fin de adjuntar controladores para eventos adjuntos.

Pasos siguientes

Ahora dispone de varias técnicas para controlar la entrada en WPF. También debería tener una mejor comprensión de los distintos tipos de eventos de entrada y los mecanismos de eventos enrutados que usa WPF.

Existen recursos adicionales que explican con más detalle los elementos del marco de trabajo WPF y el enrutamiento de eventos. Consulte los temas siguientes para obtener más información: Información general sobre comandos, Información general sobre el foco, Información general sobre elementos base, Árboles en WPF e Información general sobre eventos enrutados.

Consulte también