Поделиться через


Обработка входных данных указателя

Получайте, обрабатывайте и управляйте входными данными с указательных устройств (таких как сенсорный ввод, мышь, перо/стилус и сенсорная панель) в ваших приложениях Windows.

Это важно

Создавайте пользовательские взаимодействия только в том случае, если существует четкое, четко определенное требование и взаимодействия, поддерживаемые элементами управления платформы, не поддерживают сценарий.
Если вы настраиваете взаимодействие в приложении Windows, пользователи ожидают, что они будут согласованными, интуитивно понятными и обнаруживаемыми. По этим причинам рекомендуется моделировать пользовательские взаимодействия с теми, которые поддерживаются элементами управления платформы. Элементы управления платформой предоставляют полный интерфейс взаимодействия с пользователем приложения Windows, включая стандартные взаимодействия, анимированные физические эффекты, визуальную обратную связь и доступность.

Важные API

Pointers

Большинство взаимодействий связано с тем, что пользователь определяет объект, с которым хочет взаимодействовать, указывая на него с помощью устройств ввода, таких как сенсорный экран, мышь, перо/стилус и сенсорная панель. Поскольку необработанные данные устройства пользовательского интерфейса (HID), предоставляемые этими устройствами ввода, включают множество общих свойств, данные обрабатываются и консолидируются в единый стек входных данных и предоставляются в виде данных указателей, независимо от устройства. Затем приложения Windows могут использовать эти данные, не беспокоясь об используемом устройстве ввода.

Замечание

Сведения, относящиеся к устройству, также извлекаются из необработанных данных HID, если приложение требует их.

Каждая входная точка (или контакт) в стеке входных данных представлена объектом указателя , предоставляемым с помощью параметра PointerRoutedEventArgs в различных обработчиках событий указателя. В случае с несколькими перами или несколькими сенсорными входными данными каждый контакт рассматривается как уникальный указатель ввода.

События указателя

События указателя предоставляют основные сведения, такие как тип входного устройства и состояние обнаружения (в диапазоне или контакте), а также расширенные сведения, такие как расположение, давление и геометрия контакта. Кроме того, некоторые свойства устройства, такие как нажатие кнопки мыши пользователя или использование подсказки пера, также доступны. Если приложение должно различать устройства ввода и их возможности, см. статью "Идентификация устройств ввода".

Приложения Windows могут прослушивать следующие события указателя:

Замечание

Ограничение ввода указателя на определенный элемент пользовательского интерфейса путем вызова CapturePointer в этом элементе в обработчике событий указателя. При захвате указателя элементом только тот объект получает события ввода указателя, даже если указатель перемещается за пределы ограничивающей области объекта. IsInContact (нажатие кнопки мыши, касание или перо в контакте) должно быть истинным, чтобы CapturePointer был успешным.

Event Description

УказательОтменен

Происходит при отмене указателя платформой. Это может произойти в следующих обстоятельствах:

  • Сенсорные указатели отменяются, когда перо обнаруживается в зоне действия входной поверхности.
  • Активный контакт не обнаруживается более 100 мс.
  • Изменение монитора/дисплея (разрешение, настройки, конфигурация с несколькими мониторами).
  • Рабочий стол заблокирован или пользователь отключился.
  • Число одновременных контактов превысило число, поддерживаемого устройством.

ПотеряЗахватаУказателя

Происходит, когда другой элемент пользовательского интерфейса захватывает указатель, указатель был отпущен или другой указатель был программно захвачен.

Заметка Соответствующее событие записи указателя отсутствует.
 

Указатель вошёл

Происходит, когда указатель входит в ограничивающую область элемента. Это может произойти немного иначе для сенсорного ввода, сенсорной панели, мыши и пера.

  • Для срабатывания сенсорного ввода требуется контакт пальца, инициирующий это событие, как при прямом касании элемента, так и при перемещении в его границы.
  • Мышь и сенсорной панели имеют курсор на экране, который всегда отображается и запускает это событие, даже если кнопка мыши или сенсорной панели не нажимается.
  • Как и касание, перо вызывает это событие при прямом прижатии к элементу или при перемещении в ограничивающую область элемента. Однако перо также имеет состояние наведения указателя (IsInRange), которое, если истинно, запускает это событие.

PointerExited

Происходит, когда указатель покидает ограничивающую область элемента. Это может произойти немного иначе для сенсорного ввода, сенсорной панели, мыши и пера.

  • Требуется контакт пальца для касания, и это событие запускается, когда указатель выходит из ограничивающей области элемента.
  • Мышь и сенсорной панели имеют курсор на экране, который всегда отображается и запускает это событие, даже если кнопка мыши или сенсорной панели не нажимается.
  • Как и касание, перо вызывает это событие при выходе за пределы ограничивающей области элемента. Однако перо также имеет состояние наведения указателя мыши (IsInRange), которое запускает это событие, когда состояние изменяется с true на false.

PointerMoved

Происходит при изменении координат указателя, состояния кнопки, давления, наклона или геометрии контакта (например, ширины и высоты) в границах области элемента. Это может произойти немного иначе для сенсорного ввода, сенсорной панели, мыши и пера.

  • Касание требует прикосновения пальца и вызывает это событие только при контакте в пределах границ элемента.
  • Мышь и сенсорной панели имеют курсор на экране, который всегда отображается и запускает это событие, даже если кнопка мыши или сенсорной панели не нажимается.
  • Подобно касанию, перо инициирует это событие, соприкасаясь в пределах ограничивающей области элемента. Однако перо также имеет состояние наведения (IsInRange), которое, когда устанавливается в true и находится в пределах ограничивающей области элемента, запускает это событие.

PointerPressed

Происходит, когда указатель указывает на действие нажатия (например, когда палец касается экрана, нажатие кнопки мыши, нажатие пером или нажатие кнопки сенсорной панели) в пределах границ элемента.

Метод CapturePointer должен вызываться из обработчика для этого события.

PointerReleased

Происходит, когда указатель указывает действие выпуска (например, касание, кнопка мыши вверх, перо вверх или кнопка сенсорной панели вверх) в ограничивающей области элемента или, если указатель фиксируется, за пределами ограничивающей области.

PointerWheelChanged

Происходит при повороте колесика мыши.

Входные данные мыши связаны с одним указателем, назначенным при первом обнаружении ввода мыши. Нажатие кнопки мыши (левая, колесико или правая) создает вторичную связь между указателем и этой кнопкой через событие PointerMoved.

 

Пример события указателя

Ниже приведены фрагменты кода из базового приложения отслеживания указателей, которое показывает, как прослушивать события для нескольких указателей и получать различные свойства для связанных указателей.

Пользовательский интерфейс приложения указателя

Скачайте этот пример из примера ввода указателя (базовый)

Создание пользовательского интерфейса

В этом примере мы используем прямоугольник (Target) в качестве входных данных объекта, используюющего указатель. Цвет целевого объекта изменяется при изменении состояния указателя.

Сведения о каждом указателе отображаются в плавающем текстовом блоке , который следует указателю при перемещении. События указателя отображаются в RichTextBlock справа от прямоугольника.

Это язык разметки расширяемых приложений (XAML) для пользовательского интерфейса в этом примере.

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

Прослушивание событий указателя

В большинстве случаев мы рекомендуем получить сведения о указателе через PointerRoutedEventArgs обработчика событий.

Если аргумент события не предоставляет необходимые сведения указателя, вы можете получить доступ к расширенным данным PointerPoint , предоставляемым с помощью методов GetCurrentPoint и GetIntermediatePointsPointerRoutedEventArgs.

Следующий код настраивает глобальный объект словаря для отслеживания каждого активного указателя и определяет различные прослушиватели событий указателя для целевого объекта.

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

Обработка событий указателя

Далее мы используем обратную связь пользовательского интерфейса для демонстрации основных обработчиков событий указателя.

  • Этот обработчик управляет событием PointerPressed . Мы добавляем событие в журнал событий, добавляем указатель в активный словарь указателя и отображаем сведения указателя.

    Замечание

    События PointerPressed и PointerReleased не всегда происходят в парах. Приложение должно прослушивать и обрабатывать любое событие, которое может завершить указатель вниз (например, PointerExited, PointerCanceled и PointerCaptureLost).  

/// <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);
}
  • Этот обработчик управляет событием PointerEntered . Мы добавляем событие в журнал событий, добавляем указатель в коллекцию указателей и отображаем сведения о указателе.
/// <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);
}
  • Этот обработчик управляет событием PointerMoved . Мы добавим событие в журнал событий и обновим сведения указателя.

    Это важно

    Входные данные мыши связаны с одним указателем, назначенным при первом обнаружении ввода мыши. Нажатие кнопки мыши (левая, колесо или правая) создает вторичную связь между указателем и соответствующей кнопкой посредством события PointerPressed. Событие PointerReleased запускается только при освобождении той же кнопки мыши (пока это событие не будет завершено, кнопка не может быть связана с указателем). Из-за этой эксклюзивной связи другие нажатия кнопки мыши направляются через событие 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);
}
  • Этот обработчик управляет событием PointerWheelChanged . Мы добавим событие в журнал событий, добавьте указатель в массив указателя (при необходимости) и отобразим сведения о указателе.
/// <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);
}
  • Этот обработчик управляет событием PointerReleased , где контакт с дигитайзером завершается. Мы добавляем событие в журнал событий, удаляем указатель из коллекции указателей и обновляем сведения указателя.
/// <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);
    }
}
  • Этот обработчик управляет событием PointerExited (при обращении к дигитайзеру). Мы добавим событие в журнал событий, удалим указатель из массива указателей и обновим сведения указателя.
/// <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);
}
  • Этот обработчик управляет событием PointerCanceled . Мы добавляем событие в журнал событий, удаляем указатель из массива указателей и обновляем сведения указателя.
/// <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);
}
  • Этот обработчик управляет событием PointerCaptureLost . Мы добавляем событие в журнал событий, удаляем указатель из массива указателей и обновляем сведения об указателе.

    Замечание

    PointerCaptureLost может возникать вместо PointerReleased. Указатель может быть потерян по различным причинам, включая взаимодействие пользователя, программный захват другого указателя, вызов PointerReleased.  

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

Получение свойств указателя

Как упоминалось ранее, необходимо получить наиболее расширенные сведения о указателе из объекта Windows.UI.Input.PointerPoint , полученного с помощью методов GetCurrentPoint и GetIntermediatePointsPointerRoutedEventArgs. В следующих фрагментах кода показано, как.

  • Сначала мы создадим новый TextBlock для каждого указателя.
/// <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);
}
  • Затем мы предоставляем способ обновления сведений указателя в существующем элементе TextBlock , связанном с этим указателем.
/// <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);
            }
        }
    }
}
  • Наконец, мы запрашиваем различные свойства указателя.
/// <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;
}

Основной указатель

Некоторые устройства ввода, такие как сенсорный дигитайзер или сенсорной панели, поддерживают больше, чем типичный одинарный указатель мыши или пера (в большинстве случаев, так как Surface Hub поддерживает два ввода пера).

Используйте свойство IsPrimary класса PointerPointerProperties для идентификации и дифференцировки одного первичного указателя (основной указатель всегда является первым указателем, обнаруженным во время входной последовательности).

Определив основной указатель, его можно использовать для эмуляции ввода мыши или пера, настройки взаимодействий или предоставления других конкретных функций или пользовательского интерфейса.

Замечание

Если основной указатель освобождается, отменяется или теряется во время входной последовательности, основной указатель ввода не создается до тех пор, пока не будет инициирована новая входная последовательность (входная последовательность заканчивается, когда все указатели были освобождены, отменены или потеряны).

Пример анимации основного указателя

Эти фрагменты кода показывают, как можно предоставить специальные визуальные отзывы, чтобы помочь пользователю различать входные данные указателя в приложении.

Это конкретное приложение использует цвет и анимацию для выделения основного указателя.

Приложение указателя с анимированной визуальной обратной связью

Скачайте этот пример из примера ввода указателя (UserControl с анимацией)

Визуальный фидбэк

Мы создаем UserControl на базе объекта XAML Ellipse, который выделяет положение каждого указателя на холсте, и используем Storyboard для анимации эллипса, соответствующего основному указателю.

Ниже приведен код 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>

И вот код программной части:

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

Создание пользовательского интерфейса

Пользовательский интерфейс в этом примере ограничен входным холстом , где отслеживаем все указатели и отрисовываем индикаторы указателя и анимацию первичного указателя (если применимо), а также панель заголовка, содержащую счетчик указателя и первичный идентификатор указателя.

Ниже приведен файл 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>

Обработка событий указателя

Наконец, мы определим основные обработчики событий указателя в MainPage.xaml.cs коде. Мы не будем воспроизводить код здесь, так как основы были рассмотрены в предыдущем примере, но вы можете скачать рабочий пример из примера ввода указателя (UserControl с анимацией).

Примеры тем

Другие примеры

Архивные примеры