Windows 애플리케이션에서 포인팅 디바이스(예: 터치, 마우스, 펜/스타일러스 및 터치 패드)에서 입력 데이터를 수신, 처리 및 관리합니다.
중요합니다
명확하고 잘 정의된 요구 사항이 있고 플랫폼 컨트롤에서 지원하는 상호 작용이 시나리오를 지원하지 않는 경우에만 사용자 지정 상호 작용을 만듭니다.
Windows 애플리케이션에서 상호 작용 환경을 사용자 지정하는 경우 사용자는 일관되고 직관적이며 검색 가능할 것으로 예상합니다. 이러한 이유로, 귀하의 사용자 지정 상호작용을 플랫폼 컨트롤에서 지원하는 것에 기반하여 모델링할 것을 권장합니다. 플랫폼 컨트롤은 표준 상호 작용, 애니메이션 물리학 효과, 시각적 피드백 및 접근성을 포함하여 전체 Windows 앱 사용자 상호 작용 환경을 제공합니다.
중요 API
- Windows.Devices.Input
- Windows.UI.Input
- Windows.UI.Xaml.Input
포인터
대부분의 상호 작용 환경에는 일반적으로 사용자가 터치, 마우스, 펜/스타일러스 및 터치 패드와 같은 입력 장치를 통해 개체를 가리켜서 상호 작용하려는 개체를 식별하는 작업이 포함됩니다. 이러한 입력 디바이스에서 제공하는 원시 HID(휴먼 인터페이스 디바이스) 데이터에는 많은 공통 속성이 포함되어 있으므로 데이터는 승격되어 통합된 입력 스택으로 통합되고 디바이스에 구애받지 않는 포인터 데이터로 노출됩니다. 그러면 Windows 애플리케이션에서 사용 중인 입력 디바이스에 대해 걱정하지 않고 이 데이터를 사용할 수 있습니다.
비고
디바이스별 정보는 앱에서 필요할 경우 원시 HID 데이터에서 추출되어 사용할 수 있습니다.
입력 스택의 각 입력 지점(또는 연락처)은 다양한 포인터 이벤트 처리기의 PointerRoutedEventArgs 매개 변수를 통해 노출되는 포인터 개체로 표시됩니다. 다중 펜 또는 다중 터치 입력의 경우 각 연락처는 고유한 입력 포인터로 처리됩니다.
포인터 이벤트
포인터 이벤트는 입력 디바이스 유형 및 검색 상태(범위 또는 접촉)와 같은 기본 정보를 제공합니다. 또한, 위치, 압력, 그리고 접촉 지오메트리와 같은 확장 정보를 노출합니다. 또한 사용자가 누른 마우스 단추 또는 펜 지우개 팁이 사용되는지 여부와 같은 특정 디바이스 속성도 사용할 수 있습니다. 앱이 입력 디바이스와 해당 기능을 구분해야 하는 경우 입력 디바이스 식별을 참조하세요.
Windows 앱은 다음 포인터 이벤트를 수신 대기할 수 있습니다.
비고
포인터 이벤트 처리기 내에서 해당 요소에 대해 CapturePointer를 호출하여 특정 UI 요소에 대한 포인터 입력을 제한합니다. 포인터가 요소에 의해 캡처되는 경우 포인터가 개체의 경계 영역 밖으로 이동하더라도 해당 개체만 포인터 입력 이벤트를 받습니다. CapturePointer가 성공하려면, IsInContact(마우스 버튼이 눌린 상태, 터치 또는 스타일러스가 접촉 중이어야 함)가 true여야 합니다.
이벤트 | 설명 |
---|---|
플랫폼에서 포인터를 취소할 때 발생합니다. 이 문제는 다음과 같은 경우에 발생할 수 있습니다.
|
|
다른 UI 요소가 포인터를 차단하거나 포인터가 해제되거나, 또는 다른 포인터가 프로그래밍 방식으로 차단될 때 발생합니다.
메모 해당 포인터 캡처 이벤트가 없습니다.
|
|
포인터가 요소의 경계 영역에 들어갈 때 발생합니다. 터치, 터치 패드, 마우스 및 펜 입력에 대해 약간 다른 방식으로 발생할 수 있습니다.
|
|
포인터가 요소의 경계 영역을 떠날 때 발생합니다. 터치, 터치 패드, 마우스 및 펜 입력에 대해 약간 다른 방식으로 발생할 수 있습니다.
|
|
포인터가 요소의 경계 영역 내에서 좌표, 단추 상태, 압력, 기울기 또는 접촉 기하 도형(예: 너비 및 높이)을 변경할 때 발생합니다. 터치, 터치 패드, 마우스 및 펜 입력에 대해 약간 다른 방식으로 발생할 수 있습니다.
|
|
포인터가 요소의 경계 영역 내에서 누름 동작(예: 터치다운, 마우스 버튼 누르기, 펜 누르기 또는 터치패드 버튼 누르기)을 나타내는 경우에 발생합니다. CapturePointer는 이 이벤트의 처리기에서 호출해야 합니다. |
|
포인터가 요소의 경계 영역 내에서 해제 동작(예: 터치 해제, 마우스 버튼 해제, 펜 해제, 터치 패드 버튼 해제)을 나타내는 경우, 또는 포인터가 캡처된 경우에는 경계 영역 외부에서 발생합니다. |
|
마우스 휠을 회전할 때 발생합니다. 마우스 입력은 마우스 입력이 처음 검색될 때 할당된 단일 포인터와 연결됩니다. 마우스 단추(왼쪽, 휠 또는 오른쪽)를 클릭하면 PointerMoved 이벤트를 통해 포인터와 해당 단추 사이에 보조 연결이 만들어집니다. |
포인터 이벤트 예제
다음은 여러 포인터에 대한 이벤트를 수신 대기 및 처리하고 연결된 포인터에 대한 다양한 속성을 가져오는 방법을 보여 주는 기본 포인터 추적 앱의 코드 조각입니다.
UI 만들기
이 예제에서는 포인터 입력을 사용하는 개체로 사각형 (Target
)을 사용합니다. 포인터 상태가 변경되면 대상의 색이 변경됩니다.
각 포인터에 대한 세부 정보는 포인터와 함께 이동하는 부동 TextBlock에 표시됩니다. 포인터 이벤트 자체는 사각형 오른쪽의 RichTextBlock에서 보고된다.
이 예제의 UI에 대한 XAML(Extensible Application Markup Language)입니다.
<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를 통해서 포인터 정보를 가져오는 것이 좋습니다.
이벤트 인수가 필요한 포인터의 세부 정보를 노출하지 않는 경우, PointerRoutedEventArgs메서드의 GetCurrentPoint 및 GetIntermediatePoints 메서드를 통해 노출되는 확장된 PointerPoint 정보에 접근할 수 있습니다.
다음 코드는 각 활성 포인터를 추적하기 위한 전역 사전 개체를 설정하고 대상 개체에 대한 다양한 포인터 이벤트 수신기를 식별합니다.
// 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);
}
포인터 이벤트 처리
다음으로, UI 피드백을 사용하여 기본 포인터 이벤트 처리기를 보여 줍니다.
이 처리기는 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);
}
포인터 속성 가져오기
앞에서 설명한 대로, PointerRoutedEventArgs의 GetCurrentPoint 및 GetIntermediatePoints 메서드를 통해 얻은 Windows.UI.Input.PointerPoint 개체에서 대부분의 확장 포인터 정보를 가져와야 합니다. 다음 코드 조각은 방법을 보여줍니다.
- 먼저 각 포인터에 대해 새 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에서 두 개의 펜 입력을 지원함).
PointerPointerProperties 클래스의 읽기 전용 IsPrimary 속성을 사용하여 단일 기본 포인터를 식별하고 구분합니다(기본 포인터는 항상 입력 시퀀스 중에 검색된 첫 번째 포인터임).
기본 포인터를 식별하여 마우스 또는 펜 입력을 에뮬레이트하거나 상호 작용을 사용자 지정하거나 다른 특정 기능 또는 UI를 제공하는 데 사용할 수 있습니다.
비고
입력 시퀀스 중에 기본 포인터가 해제, 취소 또는 손실되면 새 입력 시퀀스가 시작될 때까지 기본 입력 포인터가 만들어지지 않습니다(모든 포인터가 해제, 취소 또는 손실되면 입력 시퀀스가 종료됨).
기본 포인터 애니메이션 예제
이러한 코드 조각은 사용자가 애플리케이션에서 포인터 입력을 구분하는 데 도움이 되는 특별한 시각적 피드백을 제공하는 방법을 보여 줍니다.
이 특정 앱은 색과 애니메이션을 모두 사용하여 기본 포인터를 강조 표시합니다.
이 샘플을 다운로드하세요: 포인터 입력 샘플(애니메이션이 있는 UserControl)
시각적 피드백
XAML Ellipse 개체를 기반으로 UserControl정의합니다. 이 개체는 각 포인터가 캔버스에 있는 위치를 강조 표시하고 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));
}
}
UI 만들기
이 예제의 UI는 입력 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