Freigeben über


Behandeln von Zeigereingaben

Empfangen, Verarbeiten und Verwalten von Eingabedaten von Zeigegeräten (z. B. Toucheingabe, Maus, Zeichen-/Eingabestift und Touchpad) in Ihren Windows-Anwendungen.

Von Bedeutung

Erstellen Sie benutzerdefinierte Interaktionen nur, wenn es eine klare, klar definierte Anforderung gibt und die von den Plattformsteuerelementen unterstützten Interaktionen Ihr Szenario nicht unterstützen.
Wenn Sie die Interaktionsfunktionen in Ihrer Windows-Anwendung anpassen, erwarten Benutzer, dass sie konsistent, intuitiv und auffindbar sind. Aus diesen Gründen wird empfohlen, Ihre benutzerdefinierten Interaktionen auf diejenigen zu modellieren, die von den Plattformsteuerelementenunterstützt werden. Die Plattformsteuerelemente bieten die vollständige Benutzerinteraktion von Windows-Apps, einschließlich Standardinteraktionen, animierte Physikeffekte, visuelles Feedback und Barrierefreiheit.

Wichtige APIs

Zeiger

Die meisten Interaktionsfunktionen umfassen in der Regel den Benutzer, der das Objekt identifiziert, mit dem er interagieren möchte, indem er über Eingabegeräte wie Toucheingabe, Maus, Zeichen-/Eingabestift und Touchpad darauf zeigt. Da die von diesen Eingabegeräten bereitgestellten HID-Daten (Human Interface Device) viele allgemeine Eigenschaften enthalten, werden die Daten in einen einheitlichen Eingabestapel heraufgestuft und konsolidiert und als geräteagnostische Zeigerdaten verfügbar gemacht. Ihre Windows-Anwendungen können diese Daten dann nutzen, ohne sich gedanken über das verwendete Eingabegerät zu machen.

Hinweis

Gerätespezifische Informationen werden auch aus den unformatierten HID-Daten heraufgestuft, falls die App sie benötigt.

Jeder Eingabepunkt (oder Kontakt) im Eingabestapel wird durch ein Pointer-Objekt dargestellt, das über die PointerRoutedEventArgs Parameter in den verschiedenen Zeigerereignishandlern verfügbar gemacht wird. Bei Mehrstift- oder Multi-Touch-Eingaben wird jeder Kontakt als eindeutiger Eingabezeiger behandelt.

Zeigerereignisse

Zeigerereignisse machen grundlegende Informationen wie Eingabegerätetyp und Erfassungszustand (in Reichweite oder in Kontakt) und erweiterte Informationen wie Position, Druck und Kontaktgeometrie verfügbar. Darüber hinaus sind bestimmte Geräteeigenschaften, z. B. welche Maustaste ein Benutzer gedrückt hat, oder ob die Radiererspitze des Stifts verwendet wird, ebenfalls verfügbar. Wenn Ihre App zwischen Eingabegeräten und ihren Funktionen unterscheiden muss, lesen Sie Identifizieren von Eingabegeräten.

Windows-Apps können auf die folgenden Zeigerereignisse lauschen:

Hinweis

Beschränken Sie Zeigereingaben auf ein bestimmtes UI-Element, indem Sie CapturePointer für dieses Element innerhalb eines Zeigerereignishandlers aufrufen. Wenn ein Zeiger von einem Element erfasst wird, empfängt nur dieses Objekt Zeigereingabeereignisse, auch wenn der Zeiger außerhalb des Begrenzungsbereichs des Objekts bewegt wird. Die IsInContact (Maustaste gedrückt, Toucheingabe oder Eingabestift in Kontakt) muss wahr sein, damit CapturePointer erfolgreich ist.

Ereignis BESCHREIBUNG

ZeigerCanceled

Tritt auf, wenn ein Zeiger von der Plattform zurückgesetzt wird. Dies kann unter folgenden Umständen auftreten:

  • Fingerzeiger werden deaktiviert, wenn ein Stift innerhalb der Reichweite der Eingabeoberfläche erkannt wird.
  • Ein aktiver Kontakt wird für mehr als 100 ms nicht erkannt.
  • Anzeige/Monitor wurde geändert (Auflösung, Einstellungen, Multi-Monitor-Konfiguration).
  • Der Desktop ist gesperrt oder der Benutzer hat sich abgemeldet.
  • Die Anzahl der gleichzeitigen Kontakte hat die vom Gerät unterstützte Anzahl überschritten.

ZeigererfassungVerloren

Tritt auf, wenn ein anderes UI-Element den Zeiger erfasst, der Zeiger freigegeben wurde oder ein anderer Zeiger programmgesteuert erfasst wurde.

Anmerkung Es gibt kein entsprechendes Zeigererfassungsereignis.
 

Zeigereingabe

Tritt auf, wenn ein Zeiger in den Begrenzungsbereich eines Elements eintritt. Dies kann auf etwas unterschiedliche Weise für Touch-, Touchpad-, Maus- und Stifteingaben erfolgen.

  • Die Toucheingabe erfordert einen Fingerkontakt, um dieses Ereignis auszulösen, entweder durch eine direkte Berührung des Elements oder durch das Bewegen in den Begrenzungsbereich des Elements.
  • Maus und Touchpad verfügen über einen Bildschirmcursor, der immer sichtbar ist und dieses Ereignis auslöst, auch wenn keine Maus- oder Touchpadtaste gedrückt wird.
  • Wie bei der Toucheingabe löst der Stift dieses Ereignis mit einem direkten Stiftdruck auf dem Element aus oder beim Bewegen in den Begrenzungsbereich des Elements. Der Stift verfügt jedoch auch über einen Schwebezustand (IsInRange), der, wenn es wahr ist, dieses Ereignis auslöst.

ZeigerVerlassen

Tritt auf, wenn ein Zeiger den Begrenzungsbereich eines Elements verlässt. Dies kann auf etwas unterschiedliche Weise für Touch-, Touchpad-, Maus- und Stifteingaben erfolgen.

  • Eine Berührung erfordert den Kontakt mit einem Finger und löst dieses Ereignis aus, wenn sich der Cursor aus dem Begrenzungsbereich des Elements herausbewegt.
  • Maus und Touchpad verfügen über einen Bildschirmcursor, der immer sichtbar ist und dieses Ereignis auslöst, auch wenn keine Maus- oder Touchpadtaste gedrückt wird.
  • Wie bei der Toucheingabe löst der Stift dieses Ereignis aus, wenn er den Begrenzungsbereich des Elements verlässt. Der Stift verfügt jedoch auch über einen Schwebezustand (IsInRange), der dieses Ereignis auslöst, wenn sich der Zustand von „true“ in „false“ ändert.

ZeigerBewegt

Tritt auf, wenn ein Zeiger Koordinaten, Schaltflächenzustand, Druck, Neigung oder Kontaktgeometrie (z. B. Breite und Höhe) innerhalb des Begrenzungsbereichs eines Elements ändert. Dies kann auf etwas unterschiedliche Weise für Touch-, Touchpad-, Maus- und Stifteingaben erfolgen.

  • Innerhalb des Begrenzungsbereichs des Elements erfordert die Berührung den Einsatz eines Fingers und löst dieses Ereignis nur dann aus.
  • Maus und Touchpad verfügen über einen Bildschirmcursor, der immer sichtbar ist und dieses Ereignis auslöst, auch wenn keine Maus- oder Touchpadtaste gedrückt wird.
  • Wie bei der Toucheingabe löst der Stift dieses Ereignis aus, wenn er innerhalb des Begrenzungsbereichs des Elements kontaktiert wird. Der Stift verfügt jedoch auch über einen Hoverzustand (IsInRange), der dieses Ereignis auslöst, wenn "true" und innerhalb des Begrenzungsbereichs des Elements vorhanden ist.

ZeigerGedrückt

Tritt auf, wenn der Zeiger innerhalb des Begrenzungsbereichs eines Elements eine Druckaktion erkennt (z. B. eine Toucheingabe, ein Drücken der Maustaste, eine Stiftdruckaktion oder eine Touchpadtaste).

CapturePointer- muss vom Handler für dieses Ereignis aufgerufen werden.

ZeigerFreigegeben

Tritt auf, wenn der Zeiger innerhalb des Begrenzungsbereichs eines Elements eine Loslassen-Aktion angibt (z. B. eine Touch-Up-Taste, eine Maustaste nach oben, Stift oder Touchpad-Taste nach oben), oder wenn der Zeiger außerhalb des Begrenzungsbereichs erfasst wird.

PointerWheelChanged

Tritt auf, wenn das Mausrad gedreht wird.

Die Mauseingabe wird einem einzelnen Zeiger zugeordnet, der beim ersten Erkennen der Mauseingabe zugewiesen wird. Durch Klicken auf eine Maustaste (links, Rad oder rechts) wird durch das PointerMoved-Ereignis eine sekundäre Zuordnung zwischen dem Zeiger und dieser Taste hergestellt.

 

Beispiel für Zeigerereignis

Hier sind einige Codeausschnitte aus einer einfachen Zeigerverfolgungs-App, die zeigen, wie Ereignisse für mehrere Zeiger überwacht und behandelt werden, und verschiedene Eigenschaften für die zugehörigen Zeiger abgerufen werden.

Zeigeranwendung-Benutzeroberfläche

Laden Sie dieses Beispiel aus Zeigereingabebeispiel (einfach)

Erstellen der Benutzeroberfläche

In diesem Beispiel verwenden wir ein Rechteck (Target) als Objekt, das Zeigereingaben empfängt. Die Farbe des Ziels ändert sich, wenn sich der Zeigerstatus ändert.

Details für jeden Zeiger werden in einem schwebenden TextBlock- angezeigt, der dem Zeiger folgt, während er sich bewegt. Die Zeigerereignisse selbst werden rechts neben dem Rechteck im RichTextBlock- angezeigt.

Dies ist die Extensible Application Markup Language (XAML) für die Benutzeroberfläche in diesem Beispiel.

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

Auf Zeigerereignisse lauschen

In den meisten Fällen wird empfohlen, Zeigerinformationen über die PointerRoutedEventArgs- des Ereignishandlers abzurufen.

Wenn das Ereignisargument die erforderlichen Zeigerdetails nicht verfügbar macht, können Sie Zugriff auf erweiterte PointerPoint- Informationen erhalten, die über die GetCurrentPoint- und GetIntermediatePoints- Methoden von PointerRoutedEventArgsverfügbar gemacht werden.

Der folgende Code richtet das globale Dictionary-Objekt zum Nachverfolgen jedes aktiven Zeigers ein und identifiziert die verschiedenen Pointer-Event-Listener für das Zielobjekt.

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

Bearbeitung von Zeigerereignissen

Als Nächstes verwenden wir UI-Feedback, um grundlegende Zeigerereignishandler zu veranschaulichen.

  • Dieser Handler verwaltet das --PointerPressed---Ereignis. Wir fügen das Ereignis zum Ereignisprotokoll hinzu, fügen den Zeiger zum aktiven Zeigerwörterbuch hinzu und zeigen die Zeigerdetails an.

    Hinweis

    PointerPressed und PointerReleased treten Ereignisse nicht immer paarweise auf. Ihre App sollte auf jedes Ereignis lauschen und darauf reagieren, das einen Pointer-Down-Vorgang abschließen könnte (z. B. PointerExited, PointerCanceledund 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);
}
  • Dieser Handler verwaltet das Ereignis PointerEntered. Wir fügen das Ereignis zum Ereignisprotokoll hinzu, fügen den Zeiger zur Zeigerauflistung hinzu und zeigen die Zeigerdetails an.
/// <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);
}
  • Dieser Handler verwaltet das PointerMoved--Ereignis. Wir fügen das Ereignis zum Ereignisprotokoll hinzu und aktualisieren die Zeigerdetails.

    Von Bedeutung

    Die Mauseingabe wird einem einzelnen Zeiger zugeordnet, der beim ersten Erkennen der Mauseingabe zugewiesen wird. Durch Klicken auf eine Maustaste (links, Rad oder rechts) wird eine sekundäre Zuordnung zwischen dem Zeiger und dieser Schaltfläche durch das PointerPressed-Ereignis erstellt. Das PointerReleased-Ereignis wird nur ausgelöst, wenn dieselbe Maustaste losgelassen wird (keine andere Taste kann dem Zeiger zugeordnet werden, bis dieses Ereignis abgeschlossen ist). Aufgrund dieser exklusiven Zuordnung werden andere Mausklicks durch das PointerMoved-Ereignis weitergeleitet.  

/// <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);
}
  • Dieser Handler verwaltet das PointerWheelChanged Ereignis. Wir fügen das Ereignis zum Ereignisprotokoll hinzu, fügen den Zeiger zum Zeigerarray hinzu (falls erforderlich), und zeigen die Zeigerdetails an.
/// <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);
}
  • Dieser Handler verwaltet das PointerReleased Ereignis, bei dem der Kontakt mit dem Digitalisierer beendet wird. Wir fügen das Ereignis zum Ereignisprotokoll hinzu, entfernen den Zeiger aus der Zeigerauflistung und aktualisieren die Zeigerdetails.
/// <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);
    }
}
  • Dieser Handler verwaltet das PointerExited-Ereignis (wenn der Kontakt mit dem Digitalisierer beibehalten wird). Wir fügen das Ereignis zum Ereignisprotokoll hinzu, entfernen den Zeiger aus dem Zeigerarray, und aktualisieren die Zeigerdetails.
/// <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);
}
  • Dieser Handler verwaltet das PointerCanceled-Ereignis. Wir fügen das Ereignis zum Ereignisprotokoll hinzu, entfernen den Zeiger aus dem Zeigerarray, und aktualisieren die Zeigerdetails.
/// <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);
}
  • Dieser Handler verwaltet das PointerCaptureLost--Ereignis. Wir fügen das Ereignis zum Ereignisprotokoll hinzu, entfernen den Zeiger aus dem Zeigerarray, und aktualisieren die Zeigerdetails.

    Hinweis

    PointerCaptureLost - kann anstelle von PointerReleased -auftreten. Die Zeigererfassung kann aus verschiedenen Gründen verloren gehen, einschließlich Benutzerinteraktion, programmgesteuerte Erfassung eines anderen Zeigers, Aufrufen 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);
}

Zeigereigenschaften abrufen

Wie bereits erwähnt, müssen Sie die meisten erweiterten Zeigerinformationen aus einem Windows.UI.Input.PointerPoint-Objekt abrufen, das über die GetCurrentPoint- und GetIntermediatePoints- Methoden von PointerRoutedEventArgsabgerufen wird. Die folgenden Codeausschnitte zeigen, wie das geht.

  • Zunächst erstellen wir für jeden Zeiger eine neue 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);
}
  • Anschließend bieten wir eine Möglichkeit zur Aktualisierung der Zeigerinformationen in einem vorhandenen Textblock-, der diesem Zeiger zugeordnet ist.
/// <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);
            }
        }
    }
}
  • Schließlich untersuchen wir verschiedene Zeigereigenschaften.
/// <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;
}

Primärer Zeiger

Einige Eingabegeräte, z. B. ein Touch-Digitalisierer oder Touchpad, unterstützen mehr als den üblichen einzelnen Zeiger einer Maus oder eines Stifts (in den meisten Fällen unterstützt der Surface Hub zwei Stifteingaben).

Verwenden Sie die schreibgeschützte IsPrimary-Eigenschaft der PointerPointerProperties Klasse, um einen einzelnen primären Zeiger zu identifizieren und zu unterscheiden (der primäre Zeiger ist immer der erste Zeiger, der während einer Eingabesequenz erkannt wurde).

Indem Sie den primären Zeiger identifizieren, können Sie ihn verwenden, um Maus- oder Stifteingaben zu emulieren, Interaktionen anzupassen oder eine andere bestimmte Funktionalität oder Benutzeroberfläche bereitzustellen.

Hinweis

Wenn der primäre Zeiger während einer Eingabesequenz freigegeben, abgebrochen oder verloren geht, wird kein primärer Eingabezeiger erstellt, bis eine neue Eingabesequenz begonnen wird (eine Eingabesequenz endet, wenn alle Zeiger freigegeben, abgebrochen oder verloren gegangen sind).

Beispiel für primäre Zeigeranimation

Diese Codeausschnitte zeigen, wie Sie spezielle visuelles Feedback bereitstellen können, damit ein Benutzer zwischen Zeigereingaben in Ihrer Anwendung unterscheiden kann.

Diese spezielle App verwendet sowohl Farbe als auch Animation, um den primären Zeiger hervorzuheben.

Zeigeranwendung mit animiertem visuellem Feedback

Laden Sie dieses Beispiel aus Zeigereingabebeispiel (UserControl mit Animation)

Visuelle Rückmeldung

Wir definieren ein UserControl-basierend auf einem XAML-Ellipse--Objekt, das darstellt, wo sich jeder Zeiger auf der Leinwand befindet und ein Storyboard- verwendet, um die Ellipse zu animieren, die dem primären Zeiger entspricht.

Hier sehen Sie den XAML-Code:

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

Und hier ist der Backend-Code:

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

Erstellen der Benutzeroberfläche

Die Benutzeroberfläche in diesem Beispiel ist auf die Eingabe Canvas beschränkt, wo wir alle Zeiger nachverfolgen und die Zeigerindikatoren und primäre Zeigeranimation (falls zutreffend) rendern, zusammen mit einer Kopfzeilenleiste, die einen Zeigerzähler und einen primären Zeigerbezeichner enthält.

Dies ist die Datei "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>

Bearbeitung von Zeigerereignissen

Schließlich definieren wir unsere grundlegenden Zeiger-Ereignishandler in der Code-Behind-Datei von MainPage.xaml.cs. Wir werden den Code hier nicht reproduzieren, da die Grundlagen im vorherigen Beispiel behandelt wurden, aber Sie können das Arbeitsbeispiel aus dem Zeigereingabebeispiel (UserControl mit Animation) herunterladen.

Beispiele für Themen

Weitere Beispiele

Archivbeispiele