Menangani input penunjuk

Menerima, memproses, dan mengelola data input dari perangkat penunjuk (seperti sentuhan, mouse, pena/stylus, dan touchpad) di aplikasi Windows Anda.

Penting

Buat interaksi kustom hanya jika ada persyaratan yang jelas dan terdefinisi dengan baik dan interaksi yang didukung oleh kontrol platform tidak mendukung skenario Anda.
Jika Anda menyesuaikan pengalaman interaksi di aplikasi Windows Anda, pengguna mengharapkan mereka konsisten, intuitif, dan dapat ditemukan. Untuk alasan ini, kami sarankan Anda memodelkan interaksi kustom Anda pada interaksi yang didukung oleh kontrol platform. Kontrol platform memberikan pengalaman interaksi pengguna aplikasi Windows lengkap, termasuk interaksi standar, efek fisika animasi, umpan balik visual, dan aksesibilitas.

API penting

Pointer

Sebagian besar pengalaman interaksi biasanya melibatkan pengguna yang mengidentifikasi objek yang ingin berinteraksi dengan mereka dengan mengarahkannya melalui perangkat input seperti sentuhan, mouse, pena/stylus, dan touchpad. Karena data Perangkat Antarmuka Manusia (HID) mentah yang disediakan oleh perangkat input ini mencakup banyak properti umum, data dipromosikan dan dikonsolidasikan ke dalam tumpukan input terpadu dan diekspos sebagai data penunjuk agnostik perangkat. Aplikasi Windows Anda kemudian dapat menggunakan data ini tanpa khawatir tentang perangkat input yang digunakan.

Catatan

Info khusus perangkat juga dipromosikan dari data HID mentah jika aplikasi Anda memerlukannya.

Setiap titik input (atau kontak) pada tumpukan input diwakili oleh objek Pointer yang diekspos melalui parameter PointerRoutedEventArgs di berbagai penangan peristiwa pointer. Dalam kasus input multi-pena atau multi-sentuh, setiap kontak diperlakukan sebagai penunjuk input yang unik.

Peristiwa penunjuk

Peristiwa pointer mengekspos info dasar seperti jenis perangkat input dan status deteksi (dalam rentang atau kontak), dan info yang diperluas seperti lokasi, tekanan, dan geometri kontak. Selain itu, properti perangkat tertentu seperti tombol mouse mana yang ditekan pengguna atau apakah ujung penghapus pena juga tersedia. Jika aplikasi Anda perlu membedakan antara perangkat input dan kemampuannya, lihat Mengidentifikasi perangkat input.

Aplikasi Windows dapat mendengarkan peristiwa pointer berikut:

Catatan

Batasi input penunjuk ke elemen UI tertentu dengan memanggil CapturePointer pada elemen tersebut dalam penanganan aktivitas penunjuk. Saat penunjuk ditangkap oleh elemen, hanya objek yang menerima peristiwa input pointer, bahkan ketika penunjuk bergerak di luar area pembatas objek. IsInContact (tombol mouse yang ditekan, sentuhan, atau stylus dalam kontak) harus benar agar CapturePointer berhasil.

Kejadian Deskripsi

PointerCanceled

Terjadi ketika pointer dibatalkan oleh platform. Ini dapat terjadi dalam keadaan berikut:

  • Penunjuk sentuh dibatalkan saat pena terdeteksi dalam rentang permukaan input.
  • Kontak aktif tidak terdeteksi selama lebih dari 100 ms.
  • Monitor/display diubah (resolusi, pengaturan, konfigurasi multi-mon).
  • Desktop dikunci atau pengguna telah keluar.
  • Jumlah kontak simultan melebihi nomor yang didukung oleh perangkat.

PointerCaptureLost

Terjadi ketika elemen UI lain menangkap penunjuk, penunjuk dilepaskan, atau penunjuk lain ditangkap secara terprogram.

Catatan Tidak ada peristiwa penangkapan pointer yang sesuai.
 

PointerEntered

Terjadi ketika penunjuk memasuki area pembatas elemen. Ini dapat terjadi dengan cara yang sedikit berbeda untuk sentuhan, touchpad, mouse, dan input pena.

  • Sentuhan membutuhkan kontak jari untuk menembakkan peristiwa ini, baik dari sentuhan langsung ke elemen atau dari bergerak ke area pembatas elemen.
  • Mouse dan touchpad keduanya memiliki kursor di layar yang selalu terlihat dan menembakkan peristiwa ini meskipun tidak ada tombol mouse atau touchpad yang ditekan.
  • Seperti sentuhan, pena menembakkan peristiwa ini dengan pena langsung ke bawah pada elemen atau dari bergerak ke area pembatas elemen. Namun, pena juga memiliki status hover (IsInRange) yang, ketika benar, menembakkan peristiwa ini.

PointerExited

Terjadi ketika penunjuk meninggalkan area pembatas elemen. Ini dapat terjadi dengan cara yang sedikit berbeda untuk sentuhan, touchpad, mouse, dan input pena.

  • Sentuhan membutuhkan kontak jari dan menembakkan peristiwa ini ketika penunjuk bergerak keluar dari area pembatas elemen.
  • Mouse dan touchpad keduanya memiliki kursor di layar yang selalu terlihat dan menembakkan peristiwa ini meskipun tidak ada tombol mouse atau touchpad yang ditekan.
  • Seperti sentuhan, pena menembakkan peristiwa ini ketika bergerak keluar dari area pembatas elemen. Namun, pena juga memiliki status hover (IsInRange) yang mengaktifkan peristiwa ini ketika status berubah dari true menjadi false.

PointerMoved

Terjadi ketika penunjuk berubah koordinat, status tombol, tekanan, tilt, atau geometri kontak (misalnya, lebar dan tinggi) dalam area pembatas elemen. Ini dapat terjadi dengan cara yang sedikit berbeda untuk sentuhan, touchpad, mouse, dan input pena.

  • Sentuhan memerlukan kontak jari dan menembakkan peristiwa ini hanya ketika kontak dalam area pembatas elemen.
  • Mouse dan touchpad keduanya memiliki kursor di layar yang selalu terlihat dan menembakkan peristiwa ini meskipun tidak ada tombol mouse atau touchpad yang ditekan.
  • Seperti sentuhan, pena menembakkan peristiwa ini ketika kontak dalam area pembatas elemen. Namun, pena juga memiliki status hover (IsInRange) yang, ketika benar dan di dalam area pembatas elemen, menembakkan peristiwa ini.

PointerPressed

Terjadi ketika penunjuk menunjukkan tindakan tekan (seperti sentuhan ke bawah, tombol mouse ke bawah, pena ke bawah, atau tombol touchpad ke bawah) di dalam area pembatas elemen.

CapturePointer harus dipanggil dari handler untuk kejadian ini.

PointerReleased

Terjadi ketika penunjuk menunjukkan tindakan pelepas (seperti sentuhan ke atas, tombol mouse ke atas, pena ke atas, atau tombol touchpad ke atas) di dalam area pembatas elemen atau, jika penunjuk ditangkap, di luar area pembatas.

PointerWheelChanged

Terjadi ketika roda mouse diputar.

Input mouse dikaitkan dengan satu penunjuk yang ditetapkan ketika input mouse pertama kali terdeteksi. Mengklik tombol mouse (kiri, roda, atau kanan) membuat hubungan sekunder antara penunjuk dan tombol tersebut melalui peristiwa PointerMoved .

 

Contoh peristiwa penunjuk

Berikut adalah beberapa cuplikan kode dari aplikasi pelacakan pointer dasar yang menunjukkan cara mendengarkan dan menangani peristiwa untuk beberapa pointer, dan mendapatkan berbagai properti untuk pointer terkait.

UI aplikasi penunjuk

Unduh sampel ini dari sampel input Pointer (dasar)

Membuat UI

Untuk contoh ini, kami menggunakan Persegi Panjang (Target) sebagai objek yang menggunakan input penunjuk. Warna target berubah saat status penunjuk berubah.

Detail untuk setiap penunjuk ditampilkan dalam TextBlock mengambang yang mengikuti penunjuk saat bergerak. Peristiwa pointer itu sendiri dilaporkan dalam RichTextBlock di sebelah kanan persegi panjang.

Ini adalah Extensible Application Markup Language (XAML) untuk UI dalam contoh ini.

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

Mendengarkan peristiwa pointer

Dalam kebanyakan kasus, kami sarankan Anda mendapatkan info pointer melalui PointerRoutedEventArgs dari penanganan aktivitas.

Jika argumen peristiwa tidak mengekspos detail pointer yang diperlukan, Anda bisa mendapatkan akses ke info PointerPoint yang diperluas yang diekspos melalui metode GetCurrentPoint dan GetIntermediatePoints dari PointerRoutedEventArgs.

Kode berikut menyiapkan objek kamus global untuk melacak setiap penunjuk aktif, dan mengidentifikasi berbagai pendengar peristiwa penunjuk untuk objek target.

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

Menangani peristiwa penunjuk

Selanjutnya, kami menggunakan umpan balik UI untuk menunjukkan penanganan aktivitas pointer dasar.

/// <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);
}
  • Handler ini mengelola peristiwa PointerEntered . Kami menambahkan peristiwa ke log peristiwa, menambahkan penunjuk ke koleksi penunjuk, dan menampilkan detail penunjuk.
/// <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);
}
  • Handler ini mengelola peristiwa PointerMoved . Kami menambahkan peristiwa ke log peristiwa dan memperbarui detail penunjuk.

    Penting

    Input mouse dikaitkan dengan satu penunjuk yang ditetapkan ketika input mouse pertama kali terdeteksi. Mengklik tombol mouse (kiri, roda, atau kanan) membuat hubungan sekunder antara penunjuk dan tombol itu melalui peristiwa PointerPressed . Peristiwa PointerReleased diaktifkan hanya ketika tombol mouse yang sama dilepaskan (tidak ada tombol lain yang dapat dikaitkan dengan penunjuk hingga peristiwa ini selesai). Karena asosiasi eksklusif ini, klik tombol mouse lainnya dirutekan melalui peristiwa 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);
}
  • Handler ini mengelola peristiwa PointerWheelChanged . Kami menambahkan peristiwa ke log peristiwa, menambahkan penunjuk ke array penunjuk (jika perlu), dan menampilkan detail penunjuk.
/// <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);
}
  • Handler ini mengelola peristiwa PointerReleased di mana kontak dengan digitizer dihentikan. Kami menambahkan peristiwa ke log peristiwa, menghapus penunjuk dari koleksi penunjuk, dan memperbarui detail penunjuk.
/// <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);
    }
}
  • Handler ini mengelola peristiwa PointerExited (ketika kontak dengan digitizer dipertahankan). Kami menambahkan peristiwa ke log peristiwa, menghapus penunjuk dari array penunjuk, dan memperbarui detail penunjuk.
/// <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);
}
  • Handler ini mengelola peristiwa PointerCanceled . Kami menambahkan peristiwa ke log peristiwa, menghapus penunjuk dari array penunjuk, dan memperbarui detail penunjuk.
/// <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);
}
  • Handler ini mengelola peristiwa PointerCaptureLost . Kami menambahkan peristiwa ke log peristiwa, menghapus penunjuk dari array penunjuk, dan memperbarui detail penunjuk.

    Catatan

    PointerCaptureLost dapat terjadi alih-alih PointerReleased. Penangkapan pointer dapat hilang karena berbagai alasan termasuk interaksi pengguna, pengambilan terprogram penunjuk lain, memanggil 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);
}

Mendapatkan properti penunjuk

Seperti yang dinyatakan sebelumnya, Anda harus mendapatkan info pointer yang diperluas dari objek Windows.UI.Input.PointerPoint yang diperoleh melalui metode GetCurrentPoint dan GetIntermediatePoints dari PointerRoutedEventArgs. Cuplikan kode berikut menunjukkan caranya.

  • Pertama, kita membuat TextBlock baru untuk setiap pointer.
/// <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);
}
  • Kemudian kami menyediakan cara untuk memperbarui info penunjuk di TextBlock yang ada yang terkait dengan penunjuk tersebut.
/// <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);
            }
        }
    }
}
  • Terakhir, kami mengkueri berbagai properti penunjuk.
/// <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;
}

Penunjuk utama

Beberapa perangkat input, seperti digitizer sentuh atau touchpad, mendukung lebih dari penunjuk tunggal khas mouse atau pena (dalam banyak kasus karena Surface Hub mendukung dua input pena).

Gunakan properti IsPrimary baca-saja dari kelas PointerPointerProperties untuk mengidentifikasi dan membedakan satu penunjuk utama (penunjuk utama selalu merupakan penunjuk pertama yang terdeteksi selama urutan input).

Dengan mengidentifikasi penunjuk utama, Anda dapat menggunakannya untuk meniru input mouse atau pena, menyesuaikan interaksi, atau menyediakan beberapa fungsionalitas atau UI spesifik lainnya.

Catatan

Jika penunjuk utama dilepaskan, dibatalkan, atau hilang selama urutan input, penunjuk input utama tidak dibuat sampai urutan input baru dimulai (urutan input berakhir ketika semua pointer telah dirilis, dibatalkan, atau hilang).

Contoh animasi penunjuk utama

Cuplikan kode ini menunjukkan bagaimana Anda dapat memberikan umpan balik visual khusus untuk membantu pengguna membedakan antara input pointer di aplikasi Anda.

Aplikasi khusus ini menggunakan warna dan animasi untuk menyoroti penunjuk utama.

Aplikasi pointer dengan umpan balik visual animasi

Unduh sampel ini dari sampel input Pointer (UserControl dengan animasi)

Umpan balik visual

Kami mendefinisikan UserControl, berdasarkan objek XAML Ellipse , yang menyoroti di mana setiap penunjuk berada di kanvas dan menggunakan Papan Cerita untuk menganimasikan elips yang sesuai dengan penunjuk utama.

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

Dan inilah kode-di belakang:

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

Membuat UI

UI dalam contoh ini terbatas pada Kanvas input tempat kami melacak penunjuk apa pun dan merender indikator penunjuk dan animasi penunjuk utama (jika berlaku), bersama dengan bilah header yang berisi penghitung penunjuk dan pengidentifikasi penunjuk utama.

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

Menangani peristiwa penunjuk

Terakhir, kami mendefinisikan penanganan aktivitas pointer dasar kami di kode MainPage.xaml.cs-behind. Kami tidak akan mereproduksi kode di sini karena dasar-dasarnya tercakup dalam contoh sebelumnya, tetapi Anda dapat mengunduh sampel yang berfungsi dari sampel input Pointer (UserControl dengan animasi).

Sampel topik

Sampel lainnya

Sampel arsip