Handle Zeigereingabe

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

Wichtig

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 Plattformsteuerelementen unterstü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 den PointerRoutedEventArgs-Parameter in den verschiedenen Zeigerereignishandlern verfügbar gemacht wird. Bei Multistift- oder Multitoucheingaben wird jeder Kontakt als eindeutiger Eingabezeiger behandelt.

Zeigerereignisse

Zeigerereignisse machen grundlegende Informationen wie Eingabegerätetyp und Erkennungszustand (in Bereich oder 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. Das IsInContact (gedrückte Maustaste, Toucheingabe oder Eingabestift in Kontakt) muss true sein, damit CapturePointer erfolgreich ist.

Ereignis Beschreibung

PointerCanceled

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

  • Fingereingabezeiger werden abgebrochen, wenn ein Stift innerhalb des Bereichs der Eingabeoberfläche erkannt wird.
  • Ein aktiver Kontakt wird für mehr als 100 ms nicht erkannt.
  • Monitor/Anzeige wird geändert (Auflösung, Einstellungen, Multi-Mon-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.

PointerCaptureLost

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

Hinweis: Es gibt kein entsprechendes Zeigererfassungsereignis.
 

PointerEntered

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

  • Die Toucheingabe erfordert einen Fingerkontakt, um dieses Ereignis auszuschießen, entweder von einem direkten Touchdown auf das Element oder vom 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 Stift auf dem Element aus oder wechselt in den Begrenzungsbereich des Elements. Der Stift verfügt jedoch auch über einen Hoverzustand (IsInRange), der bei "true" dieses Ereignis auslöst.

PointerExited

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.

  • Toucheingabe erfordert einen Fingerkontakt und löst dieses Ereignis aus, wenn sich der Zeiger aus dem Begrenzungsbereich des Elements bewegt.
  • 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 sich aus dem Begrenzungsbereich des Elements bewegt. Der Stift verfügt jedoch auch über einen Hoverzustand (IsInRange), der dieses Ereignis auslöst, wenn sich der Zustand von "true" in "false" ändert.

PointerMoved

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.

  • Toucheingabe erfordert einen Fingerkontakt und löst dieses Ereignis nur dann aus, wenn es sich innerhalb des Begrenzungsbereichs des Elements befindet.
  • 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 liegt.

PointerPressed

Tritt auf, wenn der Zeiger innerhalb des Begrenzungsbereichs eines Elements eine Gedrücktaktion angibt (z. B. eine Toucheingabe, eine Maustaste nach unten, einen Stift nach unten oder eine Touchpadtaste).

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

PointerReleased

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 bewegt 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 eine sekundäre Zuordnung zwischen dem Zeiger und dieser Schaltfläche durch das PointerMoved-Ereignis erstellt.

 

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.

Zeigeranwendungs-UI

Laden Sie dieses Beispiel aus dem Zeigereingabebeispiel herunter (einfach)

Erstellen der Benutzeroberfläche

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

Details für jeden Zeiger werden in einem unverankerten TextBlock angezeigt, der dem Zeiger folgt, während er verschoben wird. Die Zeigerereignisse selbst werden im RichTextBlock rechts neben dem Rechteck gemeldet.

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 den 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 Methoden "GetCurrentPoint" und "GetIntermediatePoints" von PointerRoutedEventArgs verfügbar gemacht werden.

Der folgende Code richtet das globale Wörterbuchobjekt zum Nachverfolgen jedes aktiven Zeigers ein und identifiziert die verschiedenen Zeigerereignislistener 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);
}

Behandeln von Zeigerereignissen

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

/// <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 PointerEntered-Ereignis. 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.

    Wichtig

    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 (dem Zeiger kann keine andere Schaltfläche zugeordnet werden, bis dieses Ereignis abgeschlossen ist). Aufgrund dieser exklusiven Zuordnung werden andere Mausklicks über 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 von 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);
}

Abrufen von Zeigereigenschaften

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

  • Zunächst erstellen wir für jeden Zeiger einen neuen 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 stellen wir eine Möglichkeit zum Aktualisieren der Zeigerinformationen in einem vorhandenen TextBlock bereit, 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 fragen wir verschiedene Zeigereigenschaften ab.
/// <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 Touchdigisierer oder Touchpad, unterstützen mehr als den typischen einzelnen Zeiger einer Maus oder eines Stifts (in den meisten Fällen unterstützt 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 erst ein primärer Eingabezeiger erstellt, wenn eine neue Eingabesequenz initiiert 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 dem Zeigereingabebeispiel herunter (UserControl mit Animation)

Visuelles Feedback

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

Hier ist der 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 CodeBehind:

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 den Eingabebereich beschränkt, in dem Zeigerindikatoren und primäre Zeigeranimationen (falls zutreffend) nachverfolgt und gerendert werden, zusammen mit einer Kopfzeile, 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>

Behandeln von Zeigerereignissen

Schließlich definieren wir unsere grundlegenden Zeigerereignishandler im MainPage.xaml.cs CodeBehind. 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.

Themenbeispiele

Weitere Beispiele

Archivbeispiele