Udostępnij za pomocą


Obsługa wejścia wskaźnika

Odbieranie, przetwarzanie i zarządzanie danymi wejściowymi z urządzeń wskazujących (takich jak dotyk, mysz, pióro/rysik i touchpad) w aplikacjach systemu Windows.

Ważne

Tworzenie interakcji niestandardowych tylko wtedy, gdy istnieje jasne, dobrze zdefiniowane wymaganie i interakcje obsługiwane przez kontrolki platformy nie obsługują twojego scenariusza.
Jeśli dostosujesz środowiska interakcji w aplikacji systemu Windows, użytkownicy oczekują, że będą one spójne, intuicyjne i wykrywalne. Z tych powodów zalecamy modelowanie niestandardowych interakcji na tych obsługiwanych przez kontrolki platformy. Kontrolki platformy zapewniają pełne doświadczenie interakcji użytkownika z aplikacją systemu Windows, w tym standardowe interakcje, animowane efekty fizyczne, informacje zwrotne wizualne i ułatwienia dostępu.

Ważne interfejsy API

Pointers

Większość środowisk interakcji zwykle obejmuje użytkownika identyfikującego obiekt, z którym chce korzystać, wskazując go za pośrednictwem urządzeń wejściowych, takich jak dotyk, mysz, pióro/rysik i touchpad. Ponieważ nieprzetworzone dane urządzenia interfejsu człowieka (HID) udostępniane przez te urządzenia wejściowe zawierają wiele typowych właściwości, dane są promowane i konsolidowane w ujednoliconym stosie wejściowym i udostępniane jako dane wskaźnika niezależnego od urządzeń. Aplikacje systemu Windows mogą następnie korzystać z tych danych bez obaw o używane urządzenie wejściowe.

Uwaga / Notatka

Informacje specyficzne dla urządzenia są również promowane z nieprzetworzonych danych HID, jeśli aplikacja tego wymaga.

Każdy punkt wejściowy (lub kontakt) w stosie wejściowym jest reprezentowany przez obiekt wskaźnika uwidoczniony za pośrednictwem parametru PointerRoutedEventArgs w różnych programach obsługi zdarzeń wskaźnika. W przypadku wprowadzania przy użyciu wielu piór lub wielodotykowego, każdy kontakt jest traktowany jako unikalny wskaźnik wejściowy.

Zdarzenia wskaźnika

Zdarzenia wskaźnika uwidaczniają podstawowe informacje, takie jak typ urządzenia wejściowego i stan wykrywania (w zakresie lub kontakcie), oraz rozszerzone informacje, takie jak lokalizacja, ciśnienie i geometria kontaktu. Określone właściwości urządzenia, takie jak to, który przycisk myszy jest używany przez użytkownika lub czy końcówka gumki pióra jest używana, są również dostępne. Jeśli aplikacja musi rozróżniać urządzenia wejściowe i ich możliwości, zobacz Identyfikowanie urządzeń wejściowych.

Aplikacje systemu Windows mogą nasłuchiwać następujących zdarzeń wskaźnika:

Uwaga / Notatka

Ogranicz dane wejściowe wskaźnika do określonego elementu interfejsu użytkownika, wywołując metodę CapturePointer na tym elemencie w obrębie obsługi zdarzenia wskaźnika. Gdy wskaźnik jest przechwytywany przez element, tylko ten obiekt odbiera zdarzenia wejściowe wskaźnika, nawet gdy wskaźnik porusza się poza obszarem ograniczenia obiektu. IsInContact (naciśnięcie przycisku myszy, dotyk lub kontakt rysika) musi mieć wartość 'true', aby CapturePointer zakończył się pomyślnie.

Event Description

Anulowano wskaźnik

Występuje, gdy wskaźnik (pointer) jest anulowany przez platformę. Może się to zdarzyć w następujących okolicznościach:

  • Wskaźniki dotykowe są anulowane, gdy pióro zostanie wykryte w zasięgu powierzchni wejściowej.
  • Aktywny kontakt nie jest wykrywany przez ponad 100 ms.
  • Zmieniona konfiguracja monitora/wyświetlacza (rozdzielczość, ustawienia, konfiguracja z wieloma monitorami).
  • Pulpit jest zablokowany lub użytkownik wylogował się.
  • Liczba równoczesnych kontaktów przekroczyła liczbę obsługiwaną przez urządzenie.

UtrataPrzechwytywaniaWskaźnika

Występuje, gdy inny element interfejsu użytkownika przechwytuje wskaźnik, gdy wskaźnik zostanie zwolniony lub gdy inny wskaźnik zostanie przechwycony programowo.

Uwaga Nie ma odpowiedniego zdarzenia przechwytywania wskaźnika.
 

WskaźnikWszedł

Występuje, gdy wskaźnik przechodzi do obszaru ograniczenia elementu. Może się to zdarzyć w nieco inny sposób dla dotyku, touchpada, myszy i rysika.

  • Dotyk wymaga kontaktu palca, aby uruchomić to zdarzenie, albo przez bezpośrednie dotknięcie elementu lub od przejścia do obszaru ograniczonego elementu.
  • Zarówno mysz, jak i touchpad mają kursor na ekranie, który jest zawsze widoczny i uruchamia to zdarzenie, nawet jeśli nie jest naciśnięty przycisk myszy lub touchpad.
  • Podobnie jak w przypadku dotyku, pióro uruchamia to zdarzenie przy bezpośrednim przyciśnięciu pióra na elemencie lub z przemieszczenia się do obszaru ograniczającego elementu. Jednak pióro ma również stan aktywowania (IsInRange), który, gdy ma wartość true, uruchamia to zdarzenie.

WskaźnikExited

Występuje, gdy wskaźnik opuszcza obszar graniczny elementu. Może się to zdarzyć w nieco inny sposób na dotyk, touchpad, mysz i wprowadzanie pióra.

  • Dotyk wymaga kontaktu palca i uruchamia to zdarzenie, gdy wskaźnik przechodzi poza obszar ograniczenia elementu.
  • Zarówno mysz, jak i touchpad mają kursor na ekranie, który jest zawsze widoczny i uruchamia to zdarzenie, nawet jeśli nie jest naciśnięty przycisk myszy lub touchpad.
  • Podobnie jak dotyk, pióro uruchamia to zdarzenie podczas opuszczania obszaru granicznego elementu. Jednak pióro ma również stan aktywowania (IsInRange), który uruchamia to zdarzenie, gdy stan zmieni się z true na false.

PrzesunięcieWskaźnika

Występuje, gdy wskaźnik zmienia położenie, stan przycisku, ciśnienie, pochylenie lub geometrię styku (na przykład szerokość i wysokość) w granicach elementu. Może się to zdarzyć w nieco inny sposób przy użyciu wprowadzania dotykowego, touchpada, myszy i pióra.

  • Dotyk wymaga kontaktu palca i uruchamia to zdarzenie tylko wtedy, gdy jest w kontakcie w obszarze granicznym elementu.
  • Zarówno mysz, jak i touchpad mają kursor na ekranie, który jest zawsze widoczny i uruchamia to zdarzenie, nawet jeśli nie jest naciśnięty przycisk myszy lub touchpad.
  • Podobnie jak dotyk, pióro uruchamia to zdarzenie, gdy jest w kontakcie w obszarze elementu. Jednak pióro ma również stan aktywowania (IsInRange), który w przypadku wartości true i w obszarze ograniczenia elementu powoduje wyzwolenie tego zdarzenia.

PointerPressed

Występuje, gdy wskaźnik wskazuje akcję naciśnięcia (taką jak dotknięcie, naciśnięcie przycisku myszy, dotknięcie piórem lub naciśnięcie przycisku touchpada) w obszarze graniczącym z elementem.

CapturePointer musi być wywołany przez obsługującego dla tego zdarzenia.

WskaźnikZwolniony

Występuje, gdy wskaźnik wskazuje akcję zwolnienia (na przykład puszczenie dotyku, puszczenie przycisku myszy, podniesienie pióra lub puszczenie przycisku touchpada) w obszarze ograniczającym elementu lub, jeśli wskaźnik jest przechwytywany, poza obszarem ograniczającym.

ZmienionoPokretloWskaznika

Występuje, gdy koło myszy jest obracane.

Dane wejściowe myszy są skojarzone z pojedynczym wskaźnikiem przypisanym podczas pierwszego wykrycia danych wejściowych myszy. Kliknięcie przycisku myszy (lewego, rolki lub prawego) powoduje ustanowienie drugorzędnego skojarzenia między wskaźnikiem a tym przyciskiem poprzez zdarzenie WskaźnikPrzesunięty.

 

Przykład zdarzenia wskaźnika

Poniżej przedstawiono kilka fragmentów kodu z podstawowej aplikacji do śledzenia wskaźników, która pokazuje, jak nasłuchiwać zdarzeń dla wielu wskaźników i obsługiwać je oraz pobierać różne właściwości skojarzonych wskaźników.

Interfejs użytkownika aplikacji wskaźnika

Pobierz ten przykład z przykładu danych wejściowych wskaźnika (podstawowy)

Tworzenie interfejsu użytkownika

W tym przykładzie używamy prostokąta (Target) jako obiektu zużywającego dane wejściowe wskaźnika. Kolor miejsca docelowego zmienia się po zmianie stanu wskaźnika.

Szczegóły każdego wskaźnika są wyświetlane w ruchomym polu tekstowym TextBlock, które podąża za wskaźnikiem podczas jego przesuwania. Zdarzenia wskaźników są zgłaszane w elemencie RichTextBlock po prawej stronie prostokąta.

Jest to rozszerzalny język znaczników aplikacji (XAML) interfejsu użytkownika w tym przykładzie.

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

Nasłuchiwanie zdarzeń wskaźnika

W większości przypadków zalecamy uzyskanie informacji o wskaźniku za pośrednictwem elementu PointerRoutedEventArgs programu obsługi zdarzeń.

Jeśli argument zdarzenia nie udostępnia wymaganych szczegółów wskaźnika, możesz uzyskać dostęp do rozszerzonych informacji PointerPoint udostępnionych za pośrednictwem metod GetCurrentPoint i GetIntermediatePoints w PointerRoutedEventArgs.

Poniższy kod konfiguruje obiekt słownika globalnego do śledzenia każdego aktywnego wskaźnika i identyfikuje różne odbiorniki zdarzeń wskaźnika dla obiektu docelowego.

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

Obsługa zdarzeń wskaźnika

Następnie użyjemy opinii interfejsu użytkownika, aby zademonstrować podstawowe procedury obsługi zdarzeń wskaźnika.

  • Ta procedura obsługi zarządza zdarzeniem PointerPressed . Dodajemy zdarzenie do dziennika zdarzeń, dodajemy wskaźnik do aktywnego słownika wskaźnika i wyświetlamy szczegóły wskaźnika.

    Uwaga / Notatka

    Zdarzenia PointerPressed i PointerReleased nie zawsze występują w parach. Aplikacja powinna nasłuchiwać i obsługiwać dowolne zdarzenie, które może zakończyć wciśnięcie wskaźnika (czyli PointerExited, PointerCanceled i 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);
}
  • Ten program obsługuje zdarzenie PointerEntered. Dodajemy zdarzenie do dziennika zdarzeń, dodajemy wskaźnik do kolekcji wskaźników i wyświetlamy szczegóły wskaźnika.
/// <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);
}
  • Ten obsługiwacz obsługuje zdarzenie PointerMoved. Dodajemy zdarzenie do dziennika zdarzeń i aktualizujemy szczegóły wskaźnika.

    Ważne

    Dane wejściowe myszy są skojarzone z pojedynczym wskaźnikiem przypisanym podczas pierwszego wykrycia danych wejściowych myszy. Kliknięcie przycisku myszy (lewy, środkowy lub prawy) powoduje utworzenie pomocniczego skojarzenia między wskaźnikiem a tym przyciskiem za pośrednictwem zdarzenia PointerPressed. Zdarzenie PointerReleased jest uruchamiane tylko wtedy, gdy ten sam przycisk myszy zostanie zwolniony (żaden inny przycisk nie może być skojarzony ze wskaźnikiem do momentu ukończenia tego zdarzenia). Ze względu na to wyłączne skojarzenie inne kliknięcia przycisku myszy są kierowane przez zdarzenie 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);
}
  • Ta obsługiwacz zarządza zdarzeniem PointerWheelChanged. Dodajemy zdarzenie do dziennika zdarzeń, dodajemy wskaźnik do tablicy wskaźników (w razie potrzeby) i wyświetlamy szczegóły wskaźnika.
/// <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);
}
  • Ta procedura obsługi zarządza zdarzeniem PointerReleased , w którym następuje zakończenie kontaktu z cyfryzatorem. Dodajemy zdarzenie do dziennika zdarzeń, usuwamy wskaźnik z kolekcji wskaźników i aktualizujemy szczegóły wskaźnika.
/// <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);
    }
}
  • Ta procedura obsługi zarządza zdarzeniem PointerExited (gdy kontakt z cyfryzatorem jest utrzymywany). Dodajemy zdarzenie do dziennika zdarzeń, usuwamy wskaźnik z tablicy wskaźników i aktualizujemy szczegóły wskaźnika.
/// <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);
}
  • Ta procedura obsługi zarządza zdarzeniem pointerCanceled . Dodajemy zdarzenie do dziennika zdarzeń, usuwamy wskaźnik z tablicy wskaźników i aktualizujemy szczegóły wskaźnika.
/// <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);
}
  • Ta procedura obsługi zdarzeń zarządza zdarzeniem PointerCaptureLost. Dodajemy zdarzenie do dziennika zdarzeń, usuwamy wskaźnik z tablicy wskaźników i aktualizujemy szczegóły wskaźnika.

    Uwaga / Notatka

    UtrataPrzechwyceniaWskaźnika może wystąpić zamiast ZwolnieniaWskaźnika. Przechwytywanie wskaźnika można utracić z różnych powodów, takich jak interakcja użytkownika, programowe przechwytywanie innego wskaźnika, wywoływanie funkcji 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);
}

Pobierz właściwości wskaźnika

Jak wspomniano wcześniej, należy uzyskać rozszerzone informacje o wskaźniku z obiektu Windows.UI.Input.PointerPoint uzyskanego za pomocą metod GetCurrentPoint i GetIntermediatePoints klasy PointerRoutedEventArgs. Poniższe fragmenty kodu pokazują, jak to zrobić.

/// <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);
}
  • Następnie udostępniamy sposób aktualizowania informacji wskaźnika w istniejącym elemecie TextBlock skojarzonym z tym wskaźnikiem.
/// <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);
            }
        }
    }
}
  • Na koniec odpytujemy różne właściwości wskaźnika.
/// <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;
}

Wskaźnik podstawowy

Niektóre urządzenia wejściowe, takie jak cyfrowy panel dotykowy lub touchpad, obsługują więcej niż typowy pojedynczy wskaźnik myszy lub pióra (w większości przypadków Surface Hub obsługuje dwa wejścia pióra).

Użyj właściwości IsPrimary tylko do odczytu klasy PointerPointerProperties , aby zidentyfikować i odróżnić pojedynczy wskaźnik podstawowy (podstawowy wskaźnik jest zawsze pierwszym wskaźnikiem wykrytym podczas sekwencji danych wejściowych).

Identyfikując podstawowy wskaźnik, można go użyć do emulowania danych wejściowych myszy lub pióra, dostosowywania interakcji lub udostępniania innych określonych funkcji lub interfejsu użytkownika.

Uwaga / Notatka

Jeśli podstawowy wskaźnik jest zwalniany, anulowany lub utracony podczas sekwencji danych wejściowych, podstawowy wskaźnik wejściowy nie zostanie utworzony, dopóki nie zostanie zainicjowana nowa sekwencja wejściowa (sekwencja wejściowa kończy się po zwolnieniu, anulowaniu lub utracie wszystkich wskaźników).

Przykład animacji wskaźnika podstawowego

Te fragmenty kodu pokazują, jak można zapewnić specjalną informację zwrotną wizualną, aby ułatwić użytkownikowi rozróżnienie między wejściami wskaźnika w aplikacji.

Ta konkretna aplikacja używa zarówno koloru, jak i animacji, aby wyróżnić wskaźnik podstawowy.

Aplikacja wskaźnika z animowanymi opiniami wizualnymi

Pobierz ten przykład z przykładu danych wejściowych wskaźnika (UserControl z animacją)

Opinie wizualne

Definiujemy element UserControl oparty na obiekcie wielokropka XAML, który wyróżnia miejsce, w którym każdy wskaźnik znajduje się na kanwie i używa scenorysu do animowania wielokropka odpowiadającego wskaźnikowi podstawowemu.

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

Oto kod:

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

Tworzenie interfejsu użytkownika

Interfejs użytkownika w tym przykładzie jest ograniczony do wejściowej kanwy , w której śledzimy wszystkie wskaźniki i renderujemy wskaźniki wskaźnika i animację wskaźnika podstawowego (jeśli dotyczy) wraz z paskiem nagłówka zawierającym licznik wskaźnika i identyfikatorem wskaźnika podstawowego.

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

Obsługa zdarzeń wskaźnika

Na koniec definiujemy nasze podstawowe obsługiwacze zdarzeń wskaźnika w pliku MainPage.xaml.cs jako kod zaplecza. Nie odtwórzmy tutaj kodu, ponieważ podstawy zostały omówione w poprzednim przykładzie, ale możesz pobrać przykład roboczy z przykładu wejściowego wskaźnika (UserControl z animacją).

Przykłady tematów

Inne przykłady

Próbki archiwalne