Manejar la entrada del puntero

Reciba, procese y administre datos de entrada desde dispositivos apuntados (como táctil, mouse, lápiz o lápiz táctil) en las aplicaciones de Windows.

Importante

Cree interacciones personalizadas solo si hay un requisito claro y bien definido y las interacciones admitidas por los controles de plataforma no admiten su escenario.
Si personaliza las experiencias de interacción en la aplicación windows, los usuarios esperan que sean coherentes, intuitivos y reconocibles. Por estos motivos, se recomienda modelar las interacciones personalizadas en las admitidas por los controles de plataforma. Los controles de plataforma proporcionan la experiencia completa de interacción del usuario de la aplicación de Windows, incluidas las interacciones estándar, los efectos físicos animados, los comentarios visuales y la accesibilidad.

API importantes

Punteros

La mayoría de las experiencias de interacción suelen implicar al usuario que identifica el objeto con el que quiere interactuar apuntando a ella a través de dispositivos de entrada como táctil, mouse, lápiz o lápiz táctil y panel táctil. Dado que los datos sin procesar del dispositivo de interfaz humana (HID) proporcionados por estos dispositivos de entrada incluyen muchas propiedades comunes, los datos se promueven y consolidan en una pila de entrada unificada y se exponen como datos de puntero independientes del dispositivo. Las aplicaciones de Windows pueden consumir estos datos sin preocuparse de que se use el dispositivo de entrada.

Nota:

La información específica del dispositivo también se promueve a partir de los datos HID sin procesar si la aplicación lo requiere.

Cada punto de entrada (o contacto) de la pila de entrada se representa mediante un objeto Pointer expuesto a través del parámetro PointerRoutedEventArgs en los distintos controladores de eventos de puntero. En el caso de la entrada multi-lápiz o multitáctil, cada contacto se trata como un puntero de entrada único.

Eventos de puntero

Los eventos de puntero exponen información básica, como el tipo de dispositivo de entrada y el estado de detección (en el intervalo o en contacto) y la información extendida, como la ubicación, la presión y la geometría de contacto. Además, las propiedades específicas del dispositivo, como el botón del mouse que un usuario presiona o si también se usa la sugerencia del borrador del lápiz. Si la aplicación necesita diferenciar entre los dispositivos de entrada y sus funcionalidades, consulte Identificación de dispositivos de entrada.

Las aplicaciones de Windows pueden escuchar los siguientes eventos de puntero:

Nota:

Restrinja la entrada del puntero a un elemento de interfaz de usuario específico llamando a CapturePointer en ese elemento dentro de un controlador de eventos de puntero. Cuando un elemento captura un puntero, solo ese objeto recibe eventos de entrada de puntero, incluso cuando el puntero se mueve fuera del área de límite del objeto. IsInContact (botón del mouse presionado, toque o lápiz en contacto) debe ser true para que CapturePointer se realice correctamente.

Evento Descripción

PointerCanceled

Se produce cuando la plataforma cancela un puntero. Esto puede ocurrir en las siguientes circunstancias:

  • Los punteros táctiles se cancelan cuando se detecta un lápiz dentro del intervalo de la superficie de entrada.
  • No se detecta un contacto activo para más de 100 ms.
  • La supervisión o visualización se cambia (resolución, configuración, configuración multi-mon).
  • El escritorio está bloqueado o el usuario ha cerrado la sesión.
  • El número de contactos simultáneos superó el número admitido por el dispositivo.

PointerCaptureLost

Se produce cuando otro elemento de interfaz de usuario captura el puntero, el puntero se liberó u otro puntero se capturó mediante programación.

Nota No hay ningún evento de captura del puntero correspondiente.
 

PointerEntered

Se produce cuando un puntero entra en el área de límite de un elemento. Esto puede ocurrir de maneras ligeramente diferentes para la entrada táctil, táctil, mouse y lápiz.

  • La función táctil requiere un contacto con el dedo para activar este evento, ya sea desde un toque directo en el elemento o desde el movimiento hacia el área delimitador del elemento.
  • Tanto el mouse como el panel táctil tienen un cursor en pantalla que siempre está visible y activa este evento incluso si no se presiona ningún botón del mouse o del panel táctil.
  • Al igual que la entrada táctil, el lápiz desencadena este evento con un lápiz directo hacia abajo en el elemento o desde el movimiento hacia el área de límite del elemento. Sin embargo, el lápiz también tiene un estado de desplazamiento (IsInRange) que, cuando es true, desencadena este evento.

PointerExited

Se produce cuando un puntero deja el área de límite de un elemento. Esto puede ocurrir de maneras ligeramente diferentes para la entrada táctil, táctil, mouse y lápiz.

  • La función táctil requiere un contacto con el dedo y activa este evento cuando el puntero sale del área de límite del elemento.
  • Tanto el mouse como el panel táctil tienen un cursor en pantalla que siempre está visible y activa este evento incluso si no se presiona ningún botón del mouse o del panel táctil.
  • Al igual que la entrada táctil, el lápiz activa este evento al salir del área de límite del elemento. Sin embargo, el lápiz también tiene un estado de desplazamiento (IsInRange) que activa este evento cuando el estado cambia de true a false.

PointerMoved

Se produce cuando un puntero cambia las coordenadas, el estado del botón, la presión, la inclinación o la geometría de contacto (por ejemplo, ancho y alto) dentro del área de límite de un elemento. Esto puede ocurrir de maneras ligeramente diferentes para la entrada táctil, táctil, mouse y lápiz.

  • La función táctil requiere un contacto con el dedo y activa este evento solo cuando está en contacto dentro del área de límite del elemento.
  • Tanto el mouse como el panel táctil tienen un cursor en pantalla que siempre está visible y activa este evento incluso si no se presiona ningún botón del mouse o del panel táctil.
  • Al igual que la entrada táctil, el lápiz activa este evento cuando está en contacto dentro del área de límite del elemento. Sin embargo, el lápiz también tiene un estado de desplazamiento (IsInRange) que, cuando es true y dentro del área de límite del elemento, activa este evento.

PointerPressed

Se produce cuando el puntero indica una acción de presión (como un toque hacia abajo, un botón del mouse hacia abajo, un lápiz o un botón del panel táctil) dentro del área de límite de un elemento.

Se debe llamar a CapturePointer desde el controlador para este evento.

PointerReleased

Se produce cuando el puntero indica una acción de liberación (como un toque, un botón del mouse hacia arriba, un lápiz o un botón del panel táctil) dentro del área de límite de un elemento o, si el puntero se captura, fuera del área de límite.

PointerWheelChanged

Se produce cuando se gira la rueda del mouse.

La entrada del mouse está asociada a un único puntero asignado cuando se detecta por primera vez la entrada del mouse. Al hacer clic en un botón del mouse (izquierda, rueda o derecha), se crea una asociación secundaria entre el puntero y ese botón a través del evento PointerMoved .

 

Ejemplo de evento de puntero

Estos son algunos fragmentos de código de una aplicación básica de seguimiento de punteros que muestran cómo escuchar y controlar eventos para varios punteros y obtener varias propiedades para los punteros asociados.

Interfaz de usuario de la aplicación de puntero

Descargue este ejemplo de ejemplo de ejemplo de entrada de puntero (básico)

Creación de la interfaz de usuario

En este ejemplo, usamos un rectángulo (Target) como objeto que consume la entrada del puntero. El color del destino cambia cuando cambia el estado del puntero.

Los detalles de cada puntero se muestran en un TextBlock flotante que sigue al puntero a medida que se mueve. Los propios eventos de puntero se notifican en RichTextBlock a la derecha del rectángulo.

Este es el lenguaje de marcado extensible de aplicaciones (XAML) para la interfaz de usuario de este ejemplo.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="250"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="320" />
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Canvas Name="Container" 
            Grid.Column="0"
            Grid.Row="1"
            HorizontalAlignment="Center" 
            VerticalAlignment="Center" 
            Margin="245,0" 
            Height="320"  Width="640">
        <Rectangle Name="Target" 
                    Fill="#FF0000" 
                    Stroke="Black" 
                    StrokeThickness="0"
                    Height="320" Width="640" />
    </Canvas>
    <Grid Grid.Column="1" Grid.Row="0" Grid.RowSpan="3">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Name="buttonClear" 
                Grid.Row="0"
                Content="Clear"
                Foreground="White"
                HorizontalAlignment="Stretch" 
                VerticalAlignment="Stretch">
        </Button>
        <ScrollViewer Name="eventLogScrollViewer" Grid.Row="1" 
                        VerticalScrollMode="Auto" 
                        Background="Black">                
            <RichTextBlock Name="eventLog"  
                        TextWrapping="Wrap" 
                        Foreground="#FFFFFF" 
                        ScrollViewer.VerticalScrollBarVisibility="Visible" 
                        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                        Grid.ColumnSpan="2">
            </RichTextBlock>
        </ScrollViewer>
    </Grid>
</Grid>

Escucha de eventos de puntero

En la mayoría de los casos, se recomienda obtener información del puntero a través de PointerRoutedEventArgs del controlador de eventos.

Si el argumento de evento no expone los detalles del puntero necesarios, puede obtener acceso a la información extendida de PointerPoint expuesta a través de los métodos GetCurrentPoint y GetIntermediatePoints de PointerRoutedEventArgs.

El código siguiente configura el objeto de diccionario global para realizar el seguimiento de cada puntero activo e identifica los distintos agentes de escucha de eventos de puntero para el objeto de destino.

// Dictionary to maintain information about each active pointer. 
// An entry is added during PointerPressed/PointerEntered events and removed 
// during PointerReleased/PointerCaptureLost/PointerCanceled/PointerExited events.
Dictionary<uint, Windows.UI.Xaml.Input.Pointer> pointers;

public MainPage()
{
    this.InitializeComponent();

    // Initialize the dictionary.
    pointers = new Dictionary<uint, Windows.UI.Xaml.Input.Pointer>();

    // Declare the pointer event handlers.
    Target.PointerPressed += 
        new PointerEventHandler(Target_PointerPressed);
    Target.PointerEntered += 
        new PointerEventHandler(Target_PointerEntered);
    Target.PointerReleased += 
        new PointerEventHandler(Target_PointerReleased);
    Target.PointerExited += 
        new PointerEventHandler(Target_PointerExited);
    Target.PointerCanceled += 
        new PointerEventHandler(Target_PointerCanceled);
    Target.PointerCaptureLost += 
        new PointerEventHandler(Target_PointerCaptureLost);
    Target.PointerMoved += 
        new PointerEventHandler(Target_PointerMoved);
    Target.PointerWheelChanged += 
        new PointerEventHandler(Target_PointerWheelChanged);

    buttonClear.Click += 
        new RoutedEventHandler(ButtonClear_Click);
}

Controlar eventos de puntero

A continuación, usamos comentarios de la interfaz de usuario para mostrar controladores de eventos de puntero básicos.

/// <summary>
/// The pointer pressed event handler.
/// PointerPressed and PointerReleased don't always occur in pairs. 
/// Your app should listen for and handle any event that can conclude 
/// a pointer down (PointerExited, PointerCanceled, PointerCaptureLost).
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
void Target_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Down: " + ptrPt.PointerId);

    // Lock the pointer to the target.
    Target.CapturePointer(e.Pointer);

    // Update event log.
    UpdateEventLog("Pointer captured: " + ptrPt.PointerId);

    // Check if pointer exists in dictionary (ie, enter occurred prior to press).
    if (!pointers.ContainsKey(ptrPt.PointerId))
    {
        // Add contact to dictionary.
        pointers[ptrPt.PointerId] = e.Pointer;
    }

    // Change background color of target when pointer contact detected.
    Target.Fill = new SolidColorBrush(Windows.UI.Colors.Green);

    // Display pointer details.
    CreateInfoPop(ptrPt);
}
  • Este controlador administra el evento PointerEntered. Agregamos el evento al registro de eventos, agregamos el puntero a la colección de punteros y mostramos los detalles del puntero.
/// <summary>
/// The pointer entered event handler.
/// We do not capture the pointer on this event.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Entered: " + ptrPt.PointerId);

    // Check if pointer already exists (if enter occurred prior to down).
    if (!pointers.ContainsKey(ptrPt.PointerId))
    {
        // Add contact to dictionary.
        pointers[ptrPt.PointerId] = e.Pointer;
    }

    if (pointers.Count == 0)
    {
        // Change background color of target when pointer contact detected.
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Blue);
    }

    // Display pointer details.
    CreateInfoPop(ptrPt);
}
  • Este controlador administra el evento PointerMoved. Agregamos el evento al registro de eventos y actualizamos los detalles del puntero.

    Importante

    La entrada del mouse está asociada a un único puntero asignado cuando se detecta por primera vez la entrada del mouse. Al hacer clic en un botón del mouse (izquierda, rueda o derecha), se crea una asociación secundaria entre el puntero y ese botón a través del evento PointerPressed. El evento PointerReleased se desencadena solo cuando se suelta ese mismo botón del mouse (no se puede asociar ningún otro botón al puntero hasta que se complete este evento). Debido a esta asociación exclusiva, otros clics de botón del mouse se enrutan a través del evento PointerMoved.  

/// <summary>
/// The pointer moved event handler.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Multiple, simultaneous mouse button inputs are processed here.
    // Mouse input is associated with a single pointer assigned when 
    // mouse input is first detected. 
    // Clicking additional mouse buttons (left, wheel, or right) during 
    // the interaction creates secondary associations between those buttons 
    // and the pointer through the pointer pressed event. 
    // The pointer released event is fired only when the last mouse button 
    // associated with the interaction (not necessarily the initial button) 
    // is released. 
    // Because of this exclusive association, other mouse button clicks are 
    // routed through the pointer move event.          
    if (ptrPt.PointerDevice.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
    {
        if (ptrPt.Properties.IsLeftButtonPressed)
        {
            UpdateEventLog("Left button: " + ptrPt.PointerId);
        }
        if (ptrPt.Properties.IsMiddleButtonPressed)
        {
            UpdateEventLog("Wheel button: " + ptrPt.PointerId);
        }
        if (ptrPt.Properties.IsRightButtonPressed)
        {
            UpdateEventLog("Right button: " + ptrPt.PointerId);
        }
    }

    // Display pointer details.
    UpdateInfoPop(ptrPt);
}
  • Este controlador administra el evento PointerWheelChanged. Agregamos el evento al registro de eventos, agregamos el puntero a la matriz de punteros (si es necesario) y mostramos los detalles del puntero.
/// <summary>
/// The pointer wheel event handler.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Mouse wheel: " + ptrPt.PointerId);

    // Check if pointer already exists (for example, enter occurred prior to wheel).
    if (!pointers.ContainsKey(ptrPt.PointerId))
    {
        // Add contact to dictionary.
        pointers[ptrPt.PointerId] = e.Pointer;
    }

    // Display pointer details.
    CreateInfoPop(ptrPt);
}
  • Este controlador administra el evento PointerReleased donde finaliza el contacto con el digitalizador. Agregamos el evento al registro de eventos, quitamos el puntero de la colección de punteros y actualizamos los detalles del puntero.
/// <summary>
/// The pointer released event handler.
/// PointerPressed and PointerReleased don't always occur in pairs. 
/// Your app should listen for and handle any event that can conclude 
/// a pointer down (PointerExited, PointerCanceled, PointerCaptureLost).
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
void Target_PointerReleased(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Up: " + ptrPt.PointerId);

    // If event source is mouse or touchpad and the pointer is still 
    // over the target, retain pointer and pointer details.
    // Return without removing pointer from pointers dictionary.
    // For this example, we assume a maximum of one mouse pointer.
    if (ptrPt.PointerDevice.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Mouse)
    {
        // Update target UI.
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Red);

        DestroyInfoPop(ptrPt);

        // Remove contact from dictionary.
        if (pointers.ContainsKey(ptrPt.PointerId))
        {
            pointers[ptrPt.PointerId] = null;
            pointers.Remove(ptrPt.PointerId);
        }

        // Release the pointer from the target.
        Target.ReleasePointerCapture(e.Pointer);

        // Update event log.
        UpdateEventLog("Pointer released: " + ptrPt.PointerId);
    }
    else
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Blue);
    }
}
  • Este controlador administra el evento PointerExited (cuando se mantiene el contacto con el digitalizador). Agregamos el evento al registro de eventos, quitamos el puntero de la matriz de punteros y actualizamos los detalles del puntero.
/// <summary>
/// The pointer exited event handler.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerExited(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Pointer exited: " + ptrPt.PointerId);

    // Remove contact from dictionary.
    if (pointers.ContainsKey(ptrPt.PointerId))
    {
        pointers[ptrPt.PointerId] = null;
        pointers.Remove(ptrPt.PointerId);
    }

    if (pointers.Count == 0)
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Red);
    }

    // Update the UI and pointer details.
    DestroyInfoPop(ptrPt);
}
  • Este controlador administra el evento PointerCanceled. Agregamos el evento al registro de eventos, quitamos el puntero de la matriz de punteros y actualizamos los detalles del puntero.
/// <summary>
/// The pointer canceled event handler.
/// Fires for various reasons, including: 
///    - Touch contact canceled by pen coming into range of the surface.
///    - The device doesn't report an active contact for more than 100ms.
///    - The desktop is locked or the user logged off. 
///    - The number of simultaneous contacts exceeded the number supported by the device.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerCanceled(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Pointer canceled: " + ptrPt.PointerId);

    // Remove contact from dictionary.
    if (pointers.ContainsKey(ptrPt.PointerId))
    {
        pointers[ptrPt.PointerId] = null;
        pointers.Remove(ptrPt.PointerId);
    }

    if (pointers.Count == 0)
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Black);
    }

    DestroyInfoPop(ptrPt);
}
/// <summary>
/// The pointer capture lost event handler.
/// Fires for various reasons, including: 
///    - User interactions
///    - Programmatic capture of another pointer
///    - Captured pointer was deliberately released
// PointerCaptureLost can fire instead of PointerReleased. 
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerCaptureLost(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Pointer capture lost: " + ptrPt.PointerId);

    if (pointers.Count == 0)
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Black);
    }

    // Remove contact from dictionary.
    if (pointers.ContainsKey(ptrPt.PointerId))
    {
        pointers[ptrPt.PointerId] = null;
        pointers.Remove(ptrPt.PointerId);
    }

    DestroyInfoPop(ptrPt);
}

Obtención de propiedades de puntero

Como se indicó anteriormente, debe obtener la información de puntero más extendida de un objeto Windows.UI.Input.PointerPoint obtenido a través de los métodos GetCurrentPoint y GetIntermediatePoints de PointerRoutedEventArgs. Los fragmentos de código siguientes muestran cómo.

  • En primer lugar, creamos un nuevo TextBlock para cada puntero.
/// <summary>
/// Create the pointer info popup.
/// </summary>
/// <param name="ptrPt">Reference to the input pointer.</param>
void CreateInfoPop(PointerPoint ptrPt)
{
    TextBlock pointerDetails = new TextBlock();
    pointerDetails.Name = ptrPt.PointerId.ToString();
    pointerDetails.Foreground = new SolidColorBrush(Windows.UI.Colors.White);
    pointerDetails.Text = QueryPointer(ptrPt);

    TranslateTransform x = new TranslateTransform();
    x.X = ptrPt.Position.X + 20;
    x.Y = ptrPt.Position.Y + 20;
    pointerDetails.RenderTransform = x;

    Container.Children.Add(pointerDetails);
}
  • A continuación, proporcionamos una manera de actualizar la información del puntero en un TextBlock existente asociado a ese puntero.
/// <summary>
/// Update the pointer info popup.
/// </summary>
/// <param name="ptrPt">Reference to the input pointer.</param>
void UpdateInfoPop(PointerPoint ptrPt)
{
    foreach (var pointerDetails in Container.Children)
    {
        if (pointerDetails.GetType().ToString() == "Windows.UI.Xaml.Controls.TextBlock")
        {
            TextBlock textBlock = (TextBlock)pointerDetails;
            if (textBlock.Name == ptrPt.PointerId.ToString())
            {
                // To get pointer location details, we need extended pointer info.
                // We get the pointer info through the getCurrentPoint method
                // of the event argument. 
                TranslateTransform x = new TranslateTransform();
                x.X = ptrPt.Position.X + 20;
                x.Y = ptrPt.Position.Y + 20;
                pointerDetails.RenderTransform = x;
                textBlock.Text = QueryPointer(ptrPt);
            }
        }
    }
}
  • Por último, consultamos varias propiedades de puntero.
/// <summary>
/// Get pointer details.
/// </summary>
/// <param name="ptrPt">Reference to the input pointer.</param>
/// <returns>A string composed of pointer details.</returns>
String QueryPointer(PointerPoint ptrPt)
{
    String details = "";

    switch (ptrPt.PointerDevice.PointerDeviceType)
    {
        case Windows.Devices.Input.PointerDeviceType.Mouse:
            details += "\nPointer type: mouse";
            break;
        case Windows.Devices.Input.PointerDeviceType.Pen:
            details += "\nPointer type: pen";
            if (ptrPt.IsInContact)
            {
                details += "\nPressure: " + ptrPt.Properties.Pressure;
                details += "\nrotation: " + ptrPt.Properties.Orientation;
                details += "\nTilt X: " + ptrPt.Properties.XTilt;
                details += "\nTilt Y: " + ptrPt.Properties.YTilt;
                details += "\nBarrel button pressed: " + ptrPt.Properties.IsBarrelButtonPressed;
            }
            break;
        case Windows.Devices.Input.PointerDeviceType.Touch:
            details += "\nPointer type: touch";
            details += "\nrotation: " + ptrPt.Properties.Orientation;
            details += "\nTilt X: " + ptrPt.Properties.XTilt;
            details += "\nTilt Y: " + ptrPt.Properties.YTilt;
            break;
        default:
            details += "\nPointer type: n/a";
            break;
    }

    GeneralTransform gt = Target.TransformToVisual(this);
    Point screenPoint;

    screenPoint = gt.TransformPoint(new Point(ptrPt.Position.X, ptrPt.Position.Y));
    details += "\nPointer Id: " + ptrPt.PointerId.ToString() +
        "\nPointer location (target): " + Math.Round(ptrPt.Position.X) + ", " + Math.Round(ptrPt.Position.Y) +
        "\nPointer location (container): " + Math.Round(screenPoint.X) + ", " + Math.Round(screenPoint.Y);

    return details;
}

Puntero principal

Algunos dispositivos de entrada, como un digitalizador táctil o el panel táctil, admiten más que el puntero único típico de un mouse o un lápiz (en la mayoría de los casos, como Surface Hub admite dos entradas de lápiz).

Use la propiedad IsPrimary de solo lectura de la clase PointerPointerProperties para identificar y diferenciar un único puntero principal (el puntero principal siempre es el primer puntero detectado durante una secuencia de entrada).

Al identificar el puntero principal, puede usarlo para emular la entrada del mouse o el lápiz, personalizar las interacciones o proporcionar alguna otra funcionalidad o interfaz de usuario específica.

Nota:

Si el puntero principal se libera, cancela o pierde durante una secuencia de entrada, no se crea un puntero de entrada principal hasta que se inicia una nueva secuencia de entrada (una secuencia de entrada finaliza cuando todos los punteros se han liberado, cancelado o perdido).

Ejemplo de animación de puntero principal

Estos fragmentos de código muestran cómo puede proporcionar comentarios visuales especiales para ayudar a un usuario a diferenciar entre las entradas de puntero de la aplicación.

Esta aplicación en particular usa el color y la animación para resaltar el puntero principal.

Aplicación de puntero con comentarios visuales animados

Descargue este ejemplo desde el ejemplo de entrada de puntero (UserControl con animación)

Comentarios visuales

Definimos un UserControl, basado en un objeto Ellipse XAML, que resalta dónde está cada puntero en el lienzo y usa un Guión gráfico para animar la elipse que corresponde al puntero principal.

Este es el CÓDIGO XAML:

<UserControl
    x:Class="UWP_Pointers.PointerEllipse"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UWP_Pointers"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="100"
    d:DesignWidth="100">

    <UserControl.Resources>
        <Style x:Key="EllipseStyle" TargetType="Ellipse">
            <Setter Property="Transitions">
                <Setter.Value>
                    <TransitionCollection>
                        <ContentThemeTransition/>
                    </TransitionCollection>
                </Setter.Value>
            </Setter>
        </Style>
        
        <Storyboard x:Name="myStoryboard">
            <!-- Animates the value of a Double property between 
            two target values using linear interpolation over the 
            specified Duration. -->
            <DoubleAnimation
              Storyboard.TargetName="ellipse"
              Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"  
              Duration="0:0:1" 
              AutoReverse="True" 
              RepeatBehavior="Forever" From="1.0" To="1.4">
            </DoubleAnimation>

            <!-- Animates the value of a Double property between 
            two target values using linear interpolation over the 
            specified Duration. -->
            <DoubleAnimation
              Storyboard.TargetName="ellipse"
              Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"  
              Duration="0:0:1" 
              AutoReverse="True" 
              RepeatBehavior="Forever" From="1.0" To="1.4">
            </DoubleAnimation>

            <!-- Animates the value of a Color property between 
            two target values using linear interpolation over the 
            specified Duration. -->
            <ColorAnimation 
                Storyboard.TargetName="ellipse" 
                EnableDependentAnimation="True" 
                Storyboard.TargetProperty="(Fill).(SolidColorBrush.Color)" 
                From="White" To="Red"  Duration="0:0:1" 
                AutoReverse="True" RepeatBehavior="Forever"/>
        </Storyboard>
    </UserControl.Resources>

    <Grid x:Name="CompositionContainer">
        <Ellipse Name="ellipse" 
        StrokeThickness="2" 
        Width="{x:Bind Diameter}" 
        Height="{x:Bind Diameter}"  
        Style="{StaticResource EllipseStyle}" />
    </Grid>
</UserControl>

Y este es el código subyacente:

using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

// The User Control item template is documented at 
// https://go.microsoft.com/fwlink/?LinkId=234236

namespace UWP_Pointers
{
    /// <summary>
    /// Pointer feedback object.
    /// </summary>
    public sealed partial class PointerEllipse : UserControl
    {
        // Reference to the application canvas.
        Canvas canvas;

        /// <summary>
        /// Ellipse UI for pointer feedback.
        /// </summary>
        /// <param name="c">The drawing canvas.</param>
        public PointerEllipse(Canvas c)
        {
            this.InitializeComponent();
            canvas = c;
        }

        /// <summary>
        /// Gets or sets the pointer Id to associate with the PointerEllipse object.
        /// </summary>
        public uint PointerId
        {
            get { return (uint)GetValue(PointerIdProperty); }
            set { SetValue(PointerIdProperty, value); }
        }
        // Using a DependencyProperty as the backing store for PointerId.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PointerIdProperty =
            DependencyProperty.Register("PointerId", typeof(uint), 
                typeof(PointerEllipse), new PropertyMetadata(null));


        /// <summary>
        /// Gets or sets whether the associated pointer is Primary.
        /// </summary>
        public bool PrimaryPointer
        {
            get { return (bool)GetValue(PrimaryPointerProperty); }
            set
            {
                SetValue(PrimaryPointerProperty, value);
            }
        }
        // Using a DependencyProperty as the backing store for PrimaryPointer.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PrimaryPointerProperty =
            DependencyProperty.Register("PrimaryPointer", typeof(bool), 
                typeof(PointerEllipse), new PropertyMetadata(false));


        /// <summary>
        /// Gets or sets the ellipse style based on whether the pointer is Primary.
        /// </summary>
        public bool PrimaryEllipse 
        {
            get { return (bool)GetValue(PrimaryEllipseProperty); }
            set
            {
                SetValue(PrimaryEllipseProperty, value);
                if (value)
                {
                    SolidColorBrush fillBrush = 
                        (SolidColorBrush)Application.Current.Resources["PrimaryFillBrush"];
                    SolidColorBrush strokeBrush = 
                        (SolidColorBrush)Application.Current.Resources["PrimaryStrokeBrush"];

                    ellipse.Fill = fillBrush;
                    ellipse.Stroke = strokeBrush;
                    ellipse.RenderTransform = new CompositeTransform();
                    ellipse.RenderTransformOrigin = new Point(.5, .5);
                    myStoryboard.Begin();
                }
                else
                {
                    SolidColorBrush fillBrush = 
                        (SolidColorBrush)Application.Current.Resources["SecondaryFillBrush"];
                    SolidColorBrush strokeBrush = 
                        (SolidColorBrush)Application.Current.Resources["SecondaryStrokeBrush"];
                    ellipse.Fill = fillBrush;
                    ellipse.Stroke = strokeBrush;
                }
            }
        }
        // Using a DependencyProperty as the backing store for PrimaryEllipse.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PrimaryEllipseProperty =
            DependencyProperty.Register("PrimaryEllipse", 
                typeof(bool), typeof(PointerEllipse), new PropertyMetadata(false));


        /// <summary>
        /// Gets or sets the diameter of the PointerEllipse object.
        /// </summary>
        public int Diameter
        {
            get { return (int)GetValue(DiameterProperty); }
            set { SetValue(DiameterProperty, value); }
        }
        // Using a DependencyProperty as the backing store for Diameter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DiameterProperty =
            DependencyProperty.Register("Diameter", typeof(int), 
                typeof(PointerEllipse), new PropertyMetadata(120));
    }
}

Creación de la interfaz de usuario

La interfaz de usuario de este ejemplo se limita al lienzo de entrada, donde realizamos un seguimiento de los punteros y representamos los indicadores de puntero y la animación de puntero principal (si procede), junto con una barra de encabezado que contiene un contador de puntero y un identificador de puntero principal.

Este es el archivo MainPage.xaml:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" 
                Orientation="Horizontal" 
                Grid.Row="0">
        <StackPanel.Transitions>
            <TransitionCollection>
                <AddDeleteThemeTransition/>
            </TransitionCollection>
        </StackPanel.Transitions>
        <TextBlock x:Name="Header" 
                    Text="Basic pointer tracking sample - IsPrimary" 
                    Style="{ThemeResource HeaderTextBlockStyle}" 
                    Margin="10,0,0,0" />
        <TextBlock x:Name="PointerCounterLabel"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="Number of pointers: " 
                    Margin="50,0,0,0"/>
        <TextBlock x:Name="PointerCounter"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="0" 
                    Margin="10,0,0,0"/>
        <TextBlock x:Name="PointerPrimaryLabel"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="Primary: " 
                    Margin="50,0,0,0"/>
        <TextBlock x:Name="PointerPrimary"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="n/a" 
                    Margin="10,0,0,0"/>
    </StackPanel>
    
    <Grid Grid.Row="1">
        <!--The canvas where we render the pointer UI.-->
        <Canvas x:Name="pointerCanvas"/>
    </Grid>
</Grid>

Controlar eventos de puntero

Por último, definimos nuestros controladores de eventos de puntero básicos en el código subyacente de MainPage.xaml.cs. No reproduciremos el código aquí, ya que los conceptos básicos se trataron en el ejemplo anterior, pero puede descargar el ejemplo de trabajo de ejemplo de entrada de puntero (UserControl con animación).

Ejemplos del tema

Otros ejemplos

Ejemplos de archivo