Обработка входных данных указателя
Получение, обработка и управление входными данными с устройств с указателем (например, сенсорный ввод, мышь, перо или перо или перо) и сенсорной панели) в приложениях Windows.
Внимание
Создавайте пользовательские взаимодействия только в том случае, если существует четкое, четко определенное требование и взаимодействия, поддерживаемые элементами управления платформы, не поддерживают сценарий.
Если вы настраиваете взаимодействие в приложении Windows, пользователи ожидают, что они будут согласованными, интуитивно понятными и обнаруживаемыми. По этим причинам рекомендуется моделировать пользовательские взаимодействия с теми, которые поддерживаются элементами управления платформы. Элементы управления платформой предоставляют полный интерфейс взаимодействия с пользователем приложения Windows, включая стандартные взаимодействия, анимированные физические эффекты, визуальные отзывы и специальные возможности.
Важные API
Pointers
Большинство взаимодействий обычно связаны с пользователем, определяющим объект, с которым они хотят взаимодействовать, указывая на него с помощью устройств ввода, таких как сенсорный ввод, мышь, перо или перо, перо и перо и сенсорный панель. Так как необработанные данные устройства пользовательского интерфейса (HID), предоставляемые этими устройствами ввода, включают множество общих свойств, данные повышаются и объединяются в единый стек входных данных и предоставляются в виде данных указателя устройства. Затем приложения Windows могут использовать эти данные, не беспокоясь об используемом устройстве ввода.
Примечание.
Сведения, относящиеся к устройству, также повышаются из необработанных данных HID, если приложение требует его.
Каждая входная точка (или контакт) в стеке входных данных представлена объектом указателя, предоставляемым с помощью параметра PointerRoutedEventArgs в различных обработчиках событий указателя. В случае с несколькими перами или несколькими сенсорными входными данными каждый контакт рассматривается как уникальный указатель ввода.
События указателя
События указателя предоставляют основные сведения, такие как тип входного устройства и состояние обнаружения (в диапазоне или контакте), а также расширенные сведения, такие как расположение, давление и геометрия контакта. Кроме того, некоторые свойства устройства, такие как нажатие кнопки мыши пользователя или использование подсказки пера, также доступны. Если приложение должно различать устройства ввода и их возможности, см. статью "Идентификация устройств ввода".
Приложения Windows могут прослушивать следующие события указателя:
Примечание.
Ограничение ввода указателя на определенный элемент пользовательского интерфейса путем вызова CapturePointer в этом элементе в обработчике событий указателя. При захвате указателя элементом только тот объект получает события ввода указателя, даже если указатель перемещается за пределы ограничивающей области объекта. IsInContact (нажатие кнопки мыши, касание или перо в контакте) должно быть верным для успешного выполнения CapturePointer.
Мероприятие | Description |
---|---|
Происходит при отмене указателя платформой. Это может произойти в следующих обстоятельствах:
|
|
Происходит, когда другой элемент пользовательского интерфейса фиксирует указатель, был выпущен указатель или другой указатель был программно записан.
Примечание. Соответствующее событие записи указателя отсутствует.
|
|
Происходит при вводе указателя ограничивающей области элемента. Это может произойти немного иначе для сенсорного ввода, сенсорной панели, мыши и пера.
|
|
Происходит, когда указатель покидает ограничивающую область элемента. Это может произойти немного иначе для сенсорного ввода, сенсорной панели, мыши и пера.
|
|
Происходит при изменении координат указателя, состояния кнопки, давления, наклона или контакта геометрии (например, ширины и высоты) в ограничивающей области элемента. Это может произойти немного иначе для сенсорного ввода, сенсорной панели, мыши и пера.
|
|
Происходит, когда указатель указывает действие нажатия (например, касание, кнопка мыши вниз, перо вниз или кнопка сенсорной панели вниз) в ограничивающей области элемента. Метод CapturePointer должен вызываться из обработчика для этого события. |
|
Происходит, когда указатель указывает действие выпуска (например, касание, кнопка мыши вверх, перо вверх или кнопка сенсорной панели вверх) в ограничивающей области элемента или, если указатель фиксируется, за пределами ограничивающей области. |
|
Происходит при повороте колесика мыши. Входные данные мыши связаны с одним указателем, назначенным при первом обнаружении ввода мыши. Нажатие кнопки мыши (слева, колеса или справа) создает вторичную связь между указателем и этой кнопкой через событие 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 и GetIntermediatePoints PointerRoutedEventArgs.
Следующий код настраивает глобальный объект словаря для отслеживания каждого активного указателя и определяет различные прослушиватели событий указателя для целевого объекта.
// 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 и GetIntermediatePoints PointerRoutedEventArgs. В следующих фрагментах кода показано, как.
- Сначала мы создадим новый 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, который выделяет, где каждый указатель находится на холсте и использует раскадровки для анимации многоточия, соответствующего основному указателю.
Ниже приведен код 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 с анимацией).
Связанные статьи
Примеры в статье
Другие примеры
- Базовый пример входных данных
- Пример ввода с низкой задержкой
- Пример режима взаимодействия пользователя
- Пример визуальных элементов фокуса
Архивные примеры
Windows developer