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