Información general sobre acciones del usuario

El subsistema de Windows Presentation Foundation (WPF) proporciona una API eficaz para obtener entradas de una variedad de dispositivos, como el mouse, el teclado, la entrada táctil y el lápiz óptico. En este tema se describen los servicios proporcionados por WPF y se explica la arquitectura de los sistemas de entrada.

Este tema contiene las secciones siguientes.

  • API de entrada
  • Enrutamiento de eventos
  • Controlar los eventos de entrada
  • Entrada de texto
  • Entrada táctil y manipulación
  • Focus
  • Posición del mouse
  • Captura del mouse
  • Comandos
  • El sistema de entrada y los elementos base
  • Pasos adicionales
  • Temas relacionados

API de entrada

La exposición de la API de entrada primaria se encuentra en las clases de elementos base: UIElement, ContentElement, FrameworkElement y FrameworkContentElement. Para obtener más información sobre los elementos base, vea Información general sobre elementos base. Estas clases proporcionan funcionalidad para los eventos de entrada relacionados con la presión de teclas, los botones del mouse, la rueda del mouse, el movimiento del mouse, la administración del foco y la captura del mouse, por citar algunos. Colocando 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 admitan un esquema de enrutamiento de eventos en el que más de un elemento tenga oportunidad de controlar un evento de entrada. Muchos eventos de entrada tienen un par de eventos asociados. Por ejemplo, el evento de presión de tecla está asociado a los eventos KeyDown y PreviewKeyDown. La diferencia entre estos eventos estriba en cómo se enrutan al elemento de destino. Los eventos de vista previa tunelizan en el árbol de elementos, del elemento raíz al elemento de destino. Los eventos de propagación se propagan, del elemento de destino al elemento raíz. El enrutamiento de eventos de WPF se explica de forma más detallada posteriormente en esta información general y en Información general sobre eventos enrutados.

Clases de teclado y de mouse

Además de la API de entrada de las clases de elementos base, las clases Keyboard y Mouse proporcionan una API adicional para trabajar con los datos de entrada del teclado y del mouse.

Como ejemplos de API de entrada de la clase Keyboard pueden citarse la propiedad Modifiers, que devuelve las ModifierKeys que se encuentran presionadas en estos momentos y el método IsKeyDown, que determina si está presionada una tecla especificada.

En el ejemplo siguiente se usa el método GetKeyStates para determinar si una Key se encuentra 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) And KeyStates.Down) > 0 Then
                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) & KeyStates.Down) > 0)
{
    btnNone.Background = Brushes.Red;
}

Como ejemplos de la API de entrada de la clase Mouse pueden citarse MiddleButton, que obtiene el estado del botón central del mouse, y DirectlyOver, que obtiene el elemento sobre el que se encuentra actualmente el puntero del mouse.

En el ejemplo siguiente se determina si el LeftButton del mouse se encuentra en estado Pressed.

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

Las clases Mouse y Keyboard se abordan con más detalle a lo largo de esta información general.

Entrada del lápiz

WPF dispone de compatibilidad integrada para Stylus. Stylus es un sistema de entrada de lápiz que se popularizó gracias a Tablet PC. Las aplicaciones WPF pueden considerar el lápiz como si se tratara de un mouse mediante la API del mouse, pero WPF también expone una abstracción de dispositivo de lápiz que utiliza un modelo similar al del teclado y el mouse. Todas las APIs relacionadas con el lápiz incluyen la palabra "Stylus".

Dado que el lápiz puede actuar como un mouse, las aplicaciones que sólo son compatibles con la entrada del mouse pueden obtener automáticamente cierto grado de compatibilidad con el lápiz. Cuando el lápiz se utiliza de esta forma, la aplicación tiene la oportunidad de controlar el evento de lápiz adecuado y, a continuación, controla el evento de mouse correspondiente. Además, hay otros servicios de nivel superior, como la entrada de lápiz, que también están disponibles a través de la abstracción de dispositivo de lápiz. Para obtener más información sobre la entrada de lápiz, vea Introducción a las entradas manuscritas.

Enrutamiento de eventos

Un FrameworkElement puede incluir otros elementos como elementos secundarios en su modelo de contenido, formando así un árbol de elementos. En WPF, el elemento primario puede participar en la entrada dirigida a sus elementos secundarios o a otros descendientes controlando los eventos. Esto resulta especialmente útil para crear controles partiendo de otros más pequeños, un proceso que recibe el nombre de "composición de controles" o "composición". Para obtener más información sobre los árboles de elementos y la forma en que se relacionan con las rutas de eventos, vea Árboles en WPF.

El enrutamiento de eventos es un proceso que consiste en reenviar eventos a varios elementos para que un objeto o elemento determinado a lo largo de la ruta pueda decidir si ofrece una respuesta significativa (a través de un proceso de control) a un evento que podría tener su origen en otro elemento. Los eventos enrutados utilizan uno de estos tres mecanismos de enrutamiento: directo, propagación y tunelización. En el enrutamiento directo, el elemento de origen es el único elemento que recibe notificación y el evento no se enruta a ningún otro elemento. Sin embargo, el evento enrutado directo proporciona algunas funciones adicionales que sólo están presentes para los eventos enrutados por oposición a los eventos CLR estándar. La propagación asciende por el árbol de elementos notificando primero al elemento que originó el evento, a continuación al elemento primario, y así sucesivamente. La tunelización se inicia en la raíz del árbol de elementos y desciende por este hasta finalizar en el elemento de origen inicial. Para obtener más información acerca de los eventos enrutados, vea Información general sobre eventos enrutados.

Generalmente, los eventos de entrada de WPF se presentan en parejas que constan de un evento de tunelización y de un evento de propagación. Los eventos de tunelización se distinguen de los eventos de propagación mediante 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 de ese mismo evento. Este emparejamiento de eventos es una convención que se implementa en el nivel de elemento y no es ninguna función inherente del sistema de eventos de WPF. Para obtener información detallada, consulte la sección Eventos de entrada de WPF en Información general sobre eventos enrutados.

Controlar los eventos de entrada

Para recibir la entrada en un elemento, debe existir un controlador de eventos asociado a ese evento concreto. En XAML este proceso es muy sencillo: se hace referencia al nombre del evento como un atributo del elemento que escuchará este evento. A continuación, se establece el valor del atributo en el nombre del controlador de eventos que se define, basado en 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 se producen cuando el sistema operativo notifica acciones de teclas que se producen mientras el foco del teclado se encuentra en un elemento. Los eventos de mouse y de lápiz se dividen en dos categorías: eventos que notifican cambios en la posición del puntero en relación con el elemento y eventos que notifican cambios de estado en los botones del dispositivo.

Ejemplo de evento de entrada de teclado

En el ejemplo siguiente se realizan escuchas para detectar cuándo se presiona la tecla de dirección izquierda. Se crea un elemento StackPanel con un control Button. La instancia de Button tiene asociado un controlador de eventos para escuchar cuándo se presiona la tecla de dirección izquierda.

En la primera sección del ejemplo se crean los controles StackPanel y Button, y el controlador de eventos se asocia al evento KeyDown.

<StackPanel>
  <Button Background="AliceBlue"
          KeyDown="OnButtonKeyDown"
          Content="Button1"/>
</StackPanel>
            ' 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
// 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);

La segunda sección del ejemplo está escrita en código y en ella se define el controlador de eventos. Cuando se presiona la tecla de dirección izquierda y el control Button tiene el foco de teclado, se ejecuta el controlador y cambia el color Background de Button. Si se presiona una tecla que no es tecla de dirección izquierda, el color Background de Button vuelve a establecerse en su color original.

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

Ejemplo de evento de entrada del mouse

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

En la primera sección del ejemplo se crean los controles StackPanel y Button, y se asocian controladores de eventos para los eventos MouseEnter y MouseLeave a Button.

<StackPanel>
  <Button Background="AliceBlue"
          MouseEnter="OnMouseExampleMouseEnter"
          MouseLeave="OnMosueExampleMouseLeave">Button

  </Button>
</StackPanel>
            ' 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
// 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);

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

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

Entrada de texto

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

En el caso de las acciones del teclado, WPF envía primero los eventos KeyDown o KeyUp adecuados. Si no se controlan dichos eventos y la tecla es de texto (en lugar de ser una tecla de control como las teclas de dirección o de función), se desencadena un evento TextInput. No siempre existe una asignación unívoca simple entre los eventos KeyDown o KeyUp y TextInput, ya que varias presiones de tecla pueden generar un único carácter de entrada de texto y una sola presión de tecla pueden generar cadenas de varios caracteres. Esto suele suceder con idiomas como el chino, el japonés y el coreano, que utilizan Input Method Editors (IMEs) para generar los miles de caracteres posibles de sus alfabetos correspondientes.

Cuando WPF envía un evento KeyUp o KeyDown, Key se establece en Key.System si las presiones de tecla pueden formar parte de un evento TextInput (por ejemplo, si se presiona ALT+S). Esto permite al código de un controlador de eventos KeyDown comprobar si existe Key.System y, si se encuentra, dejar el procesamiento al controlador del evento TextInput que se genera a continuación. En estos casos, pueden utilizarse las distintas propiedades del argumento TextCompositionEventArgs para determinar las presiones de tecla originales. Del mismo modo, si hay un IME activo, Key presenta el valor Key.ImeProcessed y ImeProcessedKey proporciona la presión o las presiones de tecla originales.

En el ejemplo siguiente, se define un controlador para el evento Click y otro 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.
            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
// 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);

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

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

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

En este ejemplo, la lógica de control se ha escrito dos veces, una para CTRL+O y otra para el evento de clic del botón. Esto puede simplificarse utilizando comandos en lugar de controlar directamente los eventos de entrada. Los comandos se explican en esta información general y en Información general sobre comandos.

Entrada táctil y manipulación

El nuevo hardware y API del sistema operativo Windows 7 proporcionan a las aplicaciones la capacidad de recibir varias entradas táctiles simultáneamente. WPF habilita las aplicaciones para detectar y responder a la entrada táctil de una manera similar a otra entrada, como el mouse o teclado, y generar los eventos cuando se produce la entrada táctil.

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

Requisitos previos

Necesita los siguientes componentes para desarrollar una aplicación que responda a la entrada táctil.

  • Microsoft Visual Studio 2010.

  • Windows 7.

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

Terminología

Se utilizan los términos siguientes cuando se habla de estas entradas táctiles.

  • La entrada táctil constituye un tipo de datos proporcionados por el usuario que Windows 7 reconoce. Normalmente, se inicia colocando los dedos en una pantalla sensible al tacto. Observe que dispositivos como los paneles táctiles comunes en los equipos portátiles no admiten la entrada táctil si el dispositivo solo convierte la posición y el movimiento del dedo en entrada del mouse.

  • El multitoque es la entrada táctil que se produce simultáneamente de más de un punto. Windows 7 y WPF admiten multitoque. Cuando se aborde la entrada táctil en la documentación de WPF, los conceptos se aplican a la entrada multitoque.

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

  • Un touch device representa un dispositivo que genera entrada táctil, como un dedo sobre una pantalla táctil.

Controles que responden a la entrada táctil

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

ScrollViewer define la propiedad adjunta ScrollViewer.PanningMode que le permite especificar si el desplazamiento panorámico de la entrada táctil está habilitado horizontalmente, verticalmente, ambos o ninguno. La propiedad ScrollViewer.PanningDeceleration especifica la rapidez con que el desplazamiento reduce la velocidad cuando el usuario levanta el dedo de la pantalla táctil. La propiedad adjunta ScrollViewer.PanningRatio especifica la proporción de desplazamiento para traducir el desplazamiento de la manipulación.

Eventos Touch

Las clases base, UIElement, UIElement3D y ContentElement, definen los eventos a los que se puede suscribir para que la aplicación responda a la entrada táctil. Los eventos Touch son útiles cuando la aplicación interpreta la entrada táctil como algo distinto de la manipulación de un objeto. Por ejemplo, una aplicación que permite a un usuario dibujar con uno o varios dedos se suscribiría a los eventos de toque.

Las tres clases definen los siguientes eventos, que se comportan de igual forma, sin tener en cuenta la clase definidora.

Al igual que los eventos del teclado y del mouse, los eventos Touch son eventos enrutados. Los eventos que comienzan con Preview son eventos de tunelización y los que comienzan con Touch son de propagación. Para obtener más información acerca de 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, llamando al método GetIntermediateTouchPoints o GetTouchPoint.

Para entender la interacción entre los eventos Touch, considere el escenario en el que un usuario coloca un dedo en un elemento, mueve el dedo en el elemento y, a continuación, lo quita del elemento. En la siguiente lista se muestra la ejecución de los eventos de propagación (los eventos de tunelización se omiten por cuestiones de sencillez).

Eventos Touch

La secuencia de eventos de toque.

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

  1. El evento TouchEnter se genera una vez cuando el usuario coloca un dedo en el elemento.

  2. El evento TouchDown se genera una vez.

  3. El evento TouchMove se genera varias veces según el usuario mueve el dedo dentro del elemento.

  4. El evento TouchUp se genera una vez cuando el usuario levanta el dedo del elemento.

  5. El evento TouchLeave se genera una vez.

Cuando se utilizan más de dos dedos, se genera un evento por cada uno.

Eventos de manipulación

En los casos en los que una aplicación permite a un usuario manipular un objeto, la clase UIElement define los eventos de manipulación. A diferencia de los eventos Touch que simplemente notifican la posición de la entrada táctil, los eventos de manipulación notifican cómo se puede interpretar la entrada. Hay tres tipos de manipulaciones: traducción, expansión y rotación. En la siguiente lista 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. Esto normalmente mueve el objeto.

  • Coloque dos dedos en un objeto y acerque y aleje los dedos uno de otro para invocar una manipulación de expansión. Esto normalmente cambia el tamaño del objeto.

  • Coloque dos dedos en un objeto y rote los dedos para invocar una manipulación de rotación. Esto normalmente gira el objeto.

Se puede producir más de un tipo de manipulación simultáneamente.

Cuando hace que los objetos respondan a las manipulaciones, puede lograr que parezca que el objeto tiene inercia. Así se consigue que los objetos simulen el mundo físico. Por ejemplo, si empuja con fuerza un libro por una mesa, el libro continuará moviéndose después de soltarlo. WPF le permite simular este comportamiento generando eventos de manipulación después de que los dedos del usuario suelten el objeto.

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

UIElement define los siguientes eventos de manipulación.

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

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

Considere un escenario en el que un usuario "genera" un objeto. El usuario coloca un dedo en el objeto, mueve el dedo por la pantalla táctil una distancia corta y después levanta el dedo mientras se está moviendo. El resultado es que el objeto se moverá bajo el dedo del usuario y continuará moviéndose cuando el usuario quite el dedo.

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

Eventos de manipulación

La secuencia de eventos de manipulación.

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

  1. El evento ManipulationStarting se genera cuando el usuario coloca un dedo en 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, de modo que el evento ManipulationStarting es la única vez que puede establecer esta propiedad.

  2. El evento ManipulationStarted se genera después. Este evento notifica el origen de la manipulación.

  3. El evento ManipulationDelta se genera varias veces según los dedos del usuario se mueven en una pantalla táctil. La propiedad DeltaManipulation de la clase ManipulationDeltaEventArgs notifica si la manipulación se interpreta como movimiento, expansión o traducción. Ahí es donde realiza la mayoría del trabajo de manipulación de un objeto.

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

  5. El evento ManipulationDelta se genera varias veces cuando tiene lugar la inercia. Observe que este evento se genera cuando los dedos del usuario se mueven por la pantalla táctil y cuando WPF simula la inercia. Es decir, ManipulationDelta se produce antes y después del evento ManipulationInertiaStarting. La propiedad ManipulationDeltaEventArgs.IsInertial informa de si el evento ManipulationDelta se produce durante la inercia para que se pueda comprobar esa propiedad y realizar acciones diferentes, dependiendo de su valor.

  6. El evento ManipulationCompleted se genera cuando la manipulación y la inercia finalizan. Es decir, después de que se generen todos los eventos ManipulationDelta, el evento ManipulationCompleted se genera para señalar que la manipulación se ha completado.

UIElement también define el evento ManipulationBoundaryFeedback. Este evento se genera cuando se llama al método ReportBoundaryFeedback en el evento ManipulationDelta. El evento ManipulationBoundaryFeedback permite a las aplicaciones o a los componentes proporcionar información visual cuando un objeto alcanza un límite. Por ejemplo, la clase Window controla el evento ManipulationBoundaryFeedback para hacer que la ventana se mueva ligeramente cuando se llega a su borde.

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

El evento que cancela se llama en

Los eventos del mouse que se generan para la entrada que ya se produjo

ManipulationStarting y ManipulationStarted

Eventos de presionar el mouse.

ManipulationDelta

Eventos de presionar y mover el mouse.

ManipulationInertiaStarting y ManipulationCompleted

Eventos de presionar, mover y liberar el mouse.

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

Relación entre los eventos de manipulación y toque

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

Eventos de manipulación y toque

Relación entre eventos de manipulación y toque

En la siguiente lista se describe la relación entre los eventos de manipulación y toque que se muestran en la ilustración anterior.

Focus

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 las acciones del teclado. Sólo puede haber un elemento en todo el escritorio que tenga el foco de teclado. En WPF, el elemento que tenga el foco de teclado tendrá la propiedad IsKeyboardFocused establecida en true. El método Keyboard estático FocusedElement devuelve el elemento que tiene el foco de teclado actualmente.

El foco de teclado puede obtenerse usando la tecla de tabulación hasta llegar 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. El método Focus intenta proporcionar el foco de teclado al elemento especificado. El elemento devuelto por Focus es el elemento que tiene el foco de teclado actualmente.

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

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

        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
private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}

Para obtener más información acerca del foco de teclado, vea Información general sobre el foco.

Foco lógico

El foco lógico hace referencia al FocusManager.FocusedElement de un ámbito de foco. Puede haber varios elementos con el foco lógico en una aplicación, pero sólo puede haber uno con el foco lógico en un ámbito de foco determinado.

Un ámbito de foco es un elemento contenedor que realiza un seguimiento de la propiedad FocusedElement dentro de su ámbito. Cuando el foco salga de un ámbito de foco, el elemento que tenga el foco perderá el foco de teclado, pero conservará el foco lógico. Cuando el foco regrese al ámbito de foco, el elemento que tenga el foco obtendrá el foco de teclado. Esto permite cambiar el foco de teclado entre varios ámbitos de foco, pero garantiza que el elemento que tiene el foco dentro del ámbito de foco siga siendo el elemento que tiene el foco cuando este regrese.

En Extensible Application Markup Language (XAML) un elemento puede convertirse en un ámbito de foco estableciendo la propiedad adjunta FocusManager IsFocusScope en true o mediante código, estableciendo la propiedad adjunta con el método SetIsFocusScope.

En el ejemplo siguiente, un StackPanel se convierte en un ámbito de foco 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>
            Dim focuseScope2 As New StackPanel()
            FocusManager.SetIsFocusScope(focuseScope2, True)
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);

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

Un elemento que tenga el foco de teclado también tendrá el foco lógico para el ámbito de foco al que pertenezca; por lo tanto, al establecer el foco en un elemento con el método Focus de la clase Keyboard o las clases del elemento base, se intentará asignar al elemento el foco de teclado y el foco lógico.

Para determinar cuál es el elemento que tiene el foco en un ámbito de foco, utilice el método GetFocusedElement. Para cambiar el elemento que tiene el foco para un ámbito de foco, utilice el método SetFocusedElement.

Para obtener más información acerca del foco lógico, vea Información general sobre el foco.

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? ¿Del elemento de destino de la entrada? ¿Del elemento que asoció al controlador de eventos? ¿O de algún otro? Para evitar confusiones, la API de entrada de WPF exige que se especifique el marco de referencia al trabajar con coordenadas obtenidas a través del mouse. El método GetPosition devuelve la coordenada del puntero del mouse en relación con el elemento especificado.

Captura del mouse

Los dispositivos de mouse concretamente presentan una característica modal que se denomina captura del mouse. La captura del mouse se utiliza para mantener un estado de entrada de transición cuando se inicia una operación de arrastrar y colocar, de forma que no necesariamente se produzcan otras operaciones relacionadas con la posición nominal en pantalla del puntero del mouse. Durante el arrastre, el usuario no puede hacer clic sin que se anule la operación de arrastrar y colocar, lo que hace que la mayoría de las indicaciones de mouseover sean inapropiadas mientras el origen del arrastre mantiene la captura del mouse. El sistema de entrada expone las APIs que pueden determinar el estado de captura del mouse, así como las APIs que pueden forzar la captura del mouse a un elemento concreto o borrar el estado de captura del mouse. Para obtener más información acerca de las operaciones de arrastrar y colocar, vea 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 del dispositivo. Los comandos son directivas simples, como Cut, Copy, Paste u Open. Los comandos son útiles para centralizar la lógica de comandos. Se podría obtener acceso al mismo comando desde un control Menu de ToolBar, o a través de un método abreviado de teclado. Los comandos también proporcionan un mecanismo para deshabilitar los controles cuando el comando deja de estar disponible.

RoutedCommand es la implementación WPF de la interfaz ICommand. Cuando se ejecuta RoutedCommand, se desencadenan los eventos PreviewExecuted y Executed en el destino del comando, que se tunelizan y se propagan por el árbol de elementos al igual que cualquier otra entrada. Si no se establece ningún destino de comando, el elemento que tenga el foco de teclado será el destino del comando. La lógica que ejecuta el comando está asociada a un elemento CommandBinding. Cuando un evento Executed alcanza un elemento CommandBinding para ese comando en concreto, se llama a ExecutedRoutedEventHandler en CommandBinding. Este controlador ejecuta la acción del comando.

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

WPF proporciona una biblioteca de comandos comunes que consta de ApplicationCommands, MediaCommands, ComponentCommands, NavigationCommands y EditingCommands; o también puede definir la suya propia.

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

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
            ' 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
// 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;

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

El sistema de entrada y los elementos base

Los eventos de entrada, como los eventos adjuntos definidos por las clases Mouse, Keyboard y Stylus, los desencadena el sistema de entrada y se insertan en una posición determinada del modelo de objetos basada en una prueba 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 vuelven a exponerlo las clases de elementos base UIElement y ContentElement como un evento enrutado nuevo. Los eventos enrutados de los elementos base los generan las clases que controlan el evento adjunto original y que reutilizan los datos de los eventos.

Cuando el evento de entrada se asocia a un elemento de origen determinado a través de su implementación de evento de entrada de elemento base, puede enrutarse a lo largo del resto de una ruta de eventos basada en una combinación de objetos del árbol lógico y visual, y puede controlarse mediante código de aplicación. En general, resulta más conveniente controlar estos eventos de entrada relacionados con dispositivos utilizando los eventos enrutados en UIElement y ContentElement, ya que puede utilizarse una sintaxis de controlador de eventos más intuitiva, tanto en XAML como en el código. También podría optar por controlar el evento adjunto que inició el proceso, pero tendría que enfrentarse a varios problemas: el evento adjunto podría estar marcado como controlado por el control de clases del elemento base y tendría que utilizar métodos de descriptor de acceso en lugar de una sintaxis de eventos auténticos para asociar controladores a los eventos adjuntos.

Pasos adicionales

Ya dispone de varias técnicas para controlar la entrada en WPF. También debería tener más conocimientos sobre los diversos tipos de eventos de entrada y los mecanismos de eventos enrutados utilizados por WPF.

Existen recursos adicionales en los que se explican con más detalle el enrutamiento de eventos y los elementos del marco de trabajo de WPF. Para obtener más información, vea los documentos de información general siguientes: Información general sobre comandos, Información general sobre el focoInformación general sobre elementos baseÁrboles en WPF y Información general sobre eventos enrutados.

Vea también

Conceptos

Información general sobre el foco

Información general sobre comandos

Información general sobre eventos enrutados

Información general sobre elementos base

Otros recursos

Propiedades