Treffertests in der visuellen Ebene

Dieses Thema enthält eine Übersicht über die Treffertestfunktionen der visuellen Ebene. Mit einem Treffertest können Sie bestimmen, ob ein Geometrie- oder Punktwert im Bereich des gerenderten Inhalts eines Visual-Elements liegt, sodass Sie Benutzeroberflächenverhalten, wie z.B ein Positionierungsrechteck zum Auswählen mehrerer Objekte, implementieren können.

Treffertestszenarios

Die UIElement-Klasse stellt die InputHitTest-Methode zur Verfügung, mit der Sie ein Element mit einem bestimmten Koordinatenwert testen können. In vielen Fällen stellt die InputHitTest-Methode die gewünschte Funktionalität zum Implementieren des Treffertests für Elemente bereit. Es gibt jedoch mehrere Szenarios, bei denen es möglicherweise erforderlich ist, den Treffertest auf der visuellen Ebene zu implementieren.

  • Treffertest gegen Nicht-UIElement-Objekte: Dies gilt, wenn Sie Treffertests gegen Nicht-UIElement-Objekte, wie z. B. DrawingVisual oder Grafikobjekte, durchführen.

  • Ein Treffertest unter Verwendung einer Geometrie: Dies gilt für Treffertests unter Verwendung eines Geometrieobjekts statt des Koordinatenwerts eines Punkts.

  • Ein Treffertest für mehrere Objekte: Dies gilt fürs Treffertests für mehrere Objekte, wie z.B. überlappende Objekte. Sie können Ergebnisse für alle grafischen Elemente abrufen, die eine Geometrie oder einen Punkt kreuzen, nicht nur für das erste.

  • Ignorieren der Richtlinie für den UIElement-Treffertest: Dies gilt für das Ignorieren der Richtlinie für den UIElement-Treffertest, wobei Faktoren wie die Deaktivierung oder Ausblendung von Elementen berücksichtigt werden.

Hinweis

Ein vollständiges Codebeispiel für einen Treffertest in der visuellen Ebene finden Sie unter Beispiel für einen Treffertest mit DrawingVisuals und Beispiel für einen Treffertest mit Win32-Interoperabilität.

Unterstützung für den Treffertest

Der Zweck der HitTest-Methoden in der VisualTreeHelper-Klasse besteht darin, zu bestimmen, ob sich ein Geometrie- oder Punktkoordinatenwert im Bereich des gerenderten Inhalts eines bestimmten Objekts befindet, z. B. eines Steuerelements oder eines grafischen Elements. Sie können beispielsweise den Treffertest verwenden um zu bestimmen, ob ein Mausklick innerhalb des umgebenden Rechtecks eines Objekts in die Geometrie eines Kreises fällt. Sie können auch die Standardimplementierung für den Treffertest überschreiben, um Ihre eigenen benutzerdefinierten Treffertestberechnungen durchzuführen.

Die folgende Abbildung zeigt die Beziehung zwischen dem Bereich eines nicht rechteckigen Objekts und seinem umschließenden Rechteck.

Diagram of valid hit test region
Diagramm eines gültigen Treffertestbereichs

Treffertest und Z-Reihenfolge

Die visuelle Ebene für Windows Presentation Foundation (WPF) unterstützt den Treffertest für alle Objekte unter einem Punkt oder einer Geometrie und nicht nur für das oberste Objekt. Die Ergebnisse werden in Z-Reihenfolge zurückgegeben. Das visuelle Objekt, das Sie als Parameter an die HitTest-Methode weitergeben, bestimmt jedoch, welcher Bereich der visuellen Struktur dem Treffertest unterzogen wird. Sie können einen Treffertest für die ganze visuelle Struktur oder einen beliebigen Teil davon durchführen.

In der folgenden Abbildung befindet sich das Kreisobjekt sowohl auf dem Quadrat als auch auf dem Dreieck. Wenn Sie nur einen Treffertest für das visuelle Objekt vornehmen möchten, dessen Wert für die Z-Reihenfolge der oberste ist, können Sie die Enumeration für den visuellen Treffertest so festlegen, dass er Stop von HitTestResultCallback zurückgibt, um das Durchlaufen des Treffertests nach dem ersten Element zu beenden.

Diagram of the z-order of a visual tree
Diagramm der Z-Reihenfolge einer visuellen Struktur

Wenn Sie alle visuellen Objekte unter einem bestimmten Punkt oder einer bestimmten Geometrie aufzählen möchten, kehren Sie Continue von der HitTestResultCallback zurück. Dies bedeutet, dass Sie den Treffertest sogar für vollständig von anderen Objekten überdeckte visuelle Objekte durchführen können. Weitere Informationen finden Sie im Beispielcode im Abschnitt „Verwenden eines Treffertest-Ergebnisrückrufs“.

Hinweis

Auch für transparente visuelle Objekte kann der Treffertest durchgeführt werden.

Verwenden des Standardtreffertests

Ob sich ein Punkt innerhalb der Geometrie eines Objekts befindet, können Sie mit der HitTest-Methode testen, mit der Sie ein visuelles Objekt und einen Punktkoordinatenwert für den Test angeben können. Der visuelle Objektparameter identifiziert den Ausgangspunkt in der visuellen Struktur für die Treffertestsuche. Wenn ein visuelles Objekt im visuellen Baum gefunden wird, dessen Geometrie die Koordinate enthält, wird diese auf die VisualHit-Eigenschaft eines HitTestResult-Objekts gesetzt. Der HitTestResult wird dann von der Methode HitTest zurückgegeben. Wenn der Punkt nicht in dem visuellen Teilbaum enthalten ist, den Sie gerade testen, gibt HitTestnull zurück.

Hinweis

Der Standardtreffertest gibt immer das oberste Objekt in der Z-Reihenfolge zurück. Wenn Sie alle visuellen Objekte identifizieren möchten – auch diejenigen, die teilweise oder vollständig verdeckt sind –, verwenden Sie einen Treffertest-Ergebnisrückruf.

Der Koordinatenwert, den Sie als Punktparameter an die HitTest-Methode übergeben, muss relativ zum Koordinatenraum des visuellen Objekts sein, für das Sie den Treffertest durchführen. Wenn Sie beispielsweise visuelle Objekte geschachtelt haben, die im übergeordneten Koordinatenraum bei (100, 100) definiert sind, entspricht der Treffertest eines untergeordneten visuellen Objekts bei (0, 0) dem Treffertest bei (100, 100) im übergeordneten Koordinatenraum.

Der folgende Code veranschaulicht, wie Mausereignishandler für ein UIElement-Objekt eingerichtet werden, das zum Aufzeichnen von Ereignissen für den Treffertest dient.

// Respond to the left mouse button down event by initiating the hit test.
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Perform the hit test against a given portion of the visual object tree.
    HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);

    if (result != null)
    {
        // Perform action on hit visual object.
    }
}
' Respond to the left mouse button down event by initiating the hit test.
Private Overloads Sub OnMouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
    ' Retrieve the coordinate of the mouse position.
    Dim pt As Point = e.GetPosition(CType(sender, UIElement))

    ' Perform the hit test against a given portion of the visual object tree.
    Dim result As HitTestResult = VisualTreeHelper.HitTest(myCanvas, pt)

    If result IsNot Nothing Then
        ' Perform action on hit visual object.
    End If
End Sub

Beeinflussung des Treffertests durch die visuelle Struktur

Der Startpunkt in der visuellen Struktur bestimmt, welche Objekte während der Treffertestenumeration der Objekte zurückgegeben werden. Wenn Sie für mehrere Objekte einen Treffertest durchführen möchten, muss das in der visuellen Struktur als Startpunkt verwendete Objekt das gemeinsame übergeordnete Element aller betroffenen Objekte sein. Wenn Sie beispielsweise sowohl für das Schaltflächenelement als auch für das visuelle Zeichnungsobjekt im folgenden Diagramm einen Treffertest durchführen möchten, müssen Sie den Startpunkt in der visuellen Struktur auf das gemeinsame übergeordnete Objekt für diese beiden Elemente festlegen. In diesem Fall ist das Canvas-Element das übergeordnete Objekt für das Schaltflächenelement und das visuelle Zeichnungsobjekt.

Diagram of a visual tree hierarchy
Diagramm: Hierarchie einer visuellen Struktur

Hinweis

Die Eigenschaft IsHitTestVisible ruft einen Wert ab bzw. legt ein UIElement-abgeleitetes Objekt fest, das angibt, ob dieses Element möglicherweise als Treffertestergebnis von einem Teil des gerenderten Inhalts zurückgegeben werden kann. Dadurch haben Sie die Möglichkeit, selektiv Änderungen an der visuellen Struktur vorzunehmen, um zu bestimmen, welche visuellen Objekte an einem Treffertest beteiligt sind.

Verwenden eines Treffertest-Ergebnisrückrufs

Sie können alle visuellen Objekte in einer visuellen Struktur auflisten, deren Geometrie einen angegebenen Koordinatenwert enthält. Dadurch können Sie alle visuellen Objekte identifizieren, auch diejenigen, die teilweise oder vollständig von anderen visuellen Objekten verdeckt werden. Verwenden Sie die HitTest-Methode mit einer Treffertest-Rückruffunktion, um visuelle Objekte in einer visuellen Struktur aufzulisten. Die Treffertest-Rückruffunktion wird vom System aufgerufen, wenn der von Ihnen angegebene Koordinatenwert in einem visuellen Objekt enthalten ist.

Während der Auflistung der Treffertestergebnisse sollten Sie keine Vorgänge ausführen, die die visuelle Struktur ändern. Das Hinzufügen oder Entfernen eines Objekts aus der visuellen Struktur, während diese durchlaufen wird, kann zu unvorhersehbarem Verhalten führen. Sie können den visuellen Baum gefahrlos ändern, nachdem die HitTest-Methode zurückgekehrt ist. Es kann sinnvoll sein, eine Datenstruktur anzugeben, beispielsweise ein ArrayList-Element, um während der Sammlung der Treffertestergebnisse dort Werte zu speichern.

// Respond to the right mouse button down event by setting up a hit test results callback.
private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas, null,
        new HitTestResultCallback(MyHitTestResult),
        new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        Console.WriteLine("Number of Visuals Hit: " + hitResultsList.Count);
    }
}
' Respond to the right mouse button down event by setting up a hit test results callback.
Private Overloads Sub OnMouseRightButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
    ' Retrieve the coordinate of the mouse position.
    Dim pt As Point = e.GetPosition(CType(sender, UIElement))

    ' Clear the contents of the list used for hit test results.
    hitResultsList.Clear()

    ' Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas, Nothing, New HitTestResultCallback(AddressOf MyHitTestResult), New PointHitTestParameters(pt))

    ' Perform actions on the hit test results list.
    If hitResultsList.Count > 0 Then
        Console.WriteLine("Number of Visuals Hit: " & hitResultsList.Count)
    End If
End Sub

Die Treffertest-Rückrufmethode definiert die Aktionen, die ausgeführt werden, wenn ein Treffertest für ein bestimmtes visuelles Objekt in der visuellen Struktur identifiziert wird. Nachdem diese Aktionen ausgeführt wurden, wird ein HitTestResultBehavior-Wert zurückgegeben, der bestimmt, ob die Sammlung weiterer vorhandener visueller Objekte fortgesetzt werden soll oder nicht.

// Return the result of the hit test to the callback.
public HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
    // Add the hit test result to the list that will be processed after the enumeration.
    hitResultsList.Add(result.VisualHit);

    // Set the behavior to return visuals at all z-order levels.
    return HitTestResultBehavior.Continue;
}
' Return the result of the hit test to the callback.
Public Function MyHitTestResult(ByVal result As HitTestResult) As HitTestResultBehavior
    ' Add the hit test result to the list that will be processed after the enumeration.
    hitResultsList.Add(result.VisualHit)

    ' Set the behavior to return visuals at all z-order levels.
    Return HitTestResultBehavior.Continue
End Function

Hinweis

Die Auflistung der visuellen Objekttreffer erfolgt in Z-Reihenfolge. Das visuelle Objekt auf der obersten Z-Reihenfolgenebene ist das erste aufgelistete Objekt. Alle anderen visuellen Objekte werden absteigend gemäß der Z-Reihenfolge aufgelistet. Diese Auflistungsreihenfolge entspricht der Renderingreihenfolge der grafischen Elemente.

Sie können die Sammlung visueller Objekte mit der Treffertest-Rückruffunktion jederzeit anhalten, indem Sie Stop zurückgeben.

// Set the behavior to stop enumerating visuals.
return HitTestResultBehavior.Stop;
' Set the behavior to stop enumerating visuals.
Return HitTestResultBehavior.Stop

Verwenden eines Treffertestfilter-Rückrufs

Sie können einen optionalen Treffertestfilter verwenden, um die an die Treffertestergebnisse übergebenen Objekte zu begrenzen. Dadurch können Sie die Teile der visuellen Struktur ignorieren, die nicht in Ihren Treffertestergebnissen verarbeitet werden sollen. Sie müssen eine Treffertestfilter-Rückruffunktion definieren und als Parameterwert übergeben, wenn Sie die HitTest-Methode aufrufen, um einen Treffertestfilter zu implementieren.

// Respond to the mouse wheel event by setting up a hit test filter and results enumeration.
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
    // Retrieve the coordinate of the mouse position.
    Point pt = e.GetPosition((UIElement)sender);

    // Clear the contents of the list used for hit test results.
    hitResultsList.Clear();

    // Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas,
                      new HitTestFilterCallback(MyHitTestFilter),
                      new HitTestResultCallback(MyHitTestResult),
                      new PointHitTestParameters(pt));

    // Perform actions on the hit test results list.
    if (hitResultsList.Count > 0)
    {
        ProcessHitTestResultsList();
    }
}
' Respond to the mouse wheel event by setting up a hit test filter and results enumeration.
Private Overloads Sub OnMouseWheel(ByVal sender As Object, ByVal e As MouseWheelEventArgs)
    ' Retrieve the coordinate of the mouse position.
    Dim pt As Point = e.GetPosition(CType(sender, UIElement))

    ' Clear the contents of the list used for hit test results.
    hitResultsList.Clear()

    ' Set up a callback to receive the hit test result enumeration.
    VisualTreeHelper.HitTest(myCanvas, New HitTestFilterCallback(AddressOf MyHitTestFilter), New HitTestResultCallback(AddressOf MyHitTestResult), New PointHitTestParameters(pt))

    ' Perform actions on the hit test results list.
    If hitResultsList.Count > 0 Then
        ProcessHitTestResultsList()
    End If
End Sub

Wenn Sie die optionale Treffertestfilter-Rückruffunktion nicht angeben möchten, übergeben Sie einen null-Wert als Parameter für die HitTest-Methode.

// Set up a callback to receive the hit test result enumeration,
// but no hit test filter enumeration.
VisualTreeHelper.HitTest(myCanvas,
                  null,  // No hit test filtering.
                  new HitTestResultCallback(MyHitTestResult),
                  new PointHitTestParameters(pt));
' Set up a callback to receive the hit test result enumeration,
' but no hit test filter enumeration.
VisualTreeHelper.HitTest(myCanvas, Nothing, New HitTestResultCallback(AddressOf MyHitTestResult), New PointHitTestParameters(pt)) ' No hit test filtering.

Pruning a visual tree using a hit test filter
Reduzieren der Verzweigungen in einer visuellen Struktur

Mit der Treffertestfilter-Rückruffunktion können Sie alle visuellen Objekte auflisten, deren gerenderte Inhalte die von Ihnen angegebenen Koordinaten enthalten. Sie können jedoch auch bestimmte Verzweigungen der visuellen Struktur ignorieren, die im Rahmen Ihrer Treffertestergebnis-Rückruffunktion nicht verarbeitet werden sollen. Der Rückgabewert der Treffertestfilter-Rückruffunktion bestimmt, welcher Typ von Aktion von der Enumeration der visuellen Objekte ausgeführt werden soll. Wenn beispielsweise der Wert ContinueSkipSelfAndChildren zurückgegeben wird, können Sie das aktuelle visuelle Objekt und seine untergeordneten Elemente aus der Sammlung der Treffertestergebnisse entfernen. Dies bedeutet, dass für die Treffertestergebnis-Rückruffunktion diese Objekte nicht in der Auflistung enthalten sind. Durch das Reduzieren der Verzweigungen in der visuellen Struktur von Objekten wird der Verarbeitungsaufwand während der Übergabe der Auflistung von Treffertestergebnissen verringert. Im folgenden Codebeispiel überspringt der Filter Bezeichnungen und ihre Nachfolgerelemente und führt für alle anderen Elemente Treffertests durch.

// Filter the hit test values for each object in the enumeration.
public HitTestFilterBehavior MyHitTestFilter(DependencyObject o)
{
    // Test for the object value you want to filter.
    if (o.GetType() == typeof(Label))
    {
        // Visual object and descendants are NOT part of hit test results enumeration.
        return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
    }
    else
    {
        // Visual object is part of hit test results enumeration.
        return HitTestFilterBehavior.Continue;
    }
}
' Filter the hit test values for each object in the enumeration.
Public Function MyHitTestFilter(ByVal o As DependencyObject) As HitTestFilterBehavior
    ' Test for the object value you want to filter.
    If o.GetType() Is GetType(Label) Then
        ' Visual object and descendants are NOT part of hit test results enumeration.
        Return HitTestFilterBehavior.ContinueSkipSelfAndChildren
    Else
        ' Visual object is part of hit test results enumeration.
        Return HitTestFilterBehavior.Continue
    End If
End Function

Hinweis

Der Treffertestfilter-Rückruf wird gelegentlich aufgerufen, wenn der Treffertestergebnis-Rückruf nicht aufgerufen wird.

Überschreiben des Standardtreffertests

Sie können die Unterstützung des Standardtreffertests für ein visuelles Objekt überschreiben, indem Sie die HitTestCore-Methode überschreiben. Das heißt, wenn Sie die HitTest-Methode aufrufen, wird Ihre überschriebene Implementierung von HitTestCore aufgerufen. Ihre überschriebene Methode wird aufgerufen, wenn sich ein Treffertest innerhalb des umgebenden Rechtecks für das visuelle Objekt befindet. Dies gilt auch dann, wenn die Koordinate außerhalb des gerenderten Inhalts des visuellen Objekts liegt.

// Override default hit test support in visual object.
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
    Point pt = hitTestParameters.HitPoint;

    // Perform custom actions during the hit test processing,
    // which may include verifying that the point actually
    // falls within the rendered content of the visual.

    // Return hit on bounding rectangle of visual object.
    return new PointHitTestResult(this, pt);
}
' Override default hit test support in visual object.
Protected Overrides Overloads Function HitTestCore(ByVal hitTestParameters As PointHitTestParameters) As HitTestResult
    Dim pt As Point = hitTestParameters.HitPoint

    ' Perform custom actions during the hit test processing,
    ' which may include verifying that the point actually
    ' falls within the rendered content of the visual.

    ' Return hit on bounding rectangle of visual object.
    Return New PointHitTestResult(Me, pt)
End Function

Es kann vorkommen, dass Sie einen Treffertest sowohl für das umschließende Rechteck als auch für den gerenderten Inhalt eines visuellen Objekts durchführen möchten. Indem Sie den PointHitTestParameters-Parameterwert in der überschriebenen HitTestCore-Methode als Parameter für die Basismethode HitTestCore verwenden, können Sie, basierend auf einem Treffer für das umschließende Rechteck eines visuellen Objekts, Aktionen ausführen und anschließend einen zweiten Treffertest für den gerenderten Inhalt des visuellen Objekts vornehmen.

// Override default hit test support in visual object.
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
    // Perform actions based on hit test of bounding rectangle.
    // ...

    // Return results of base class hit testing,
    // which only returns hit on the geometry of visual objects.
    return base.HitTestCore(hitTestParameters);
}
' Override default hit test support in visual object.
Protected Overrides Overloads Function HitTestCore(ByVal hitTestParameters As PointHitTestParameters) As HitTestResult
    ' Perform actions based on hit test of bounding rectangle.
    ' ...

    ' Return results of base class hit testing,
    ' which only returns hit on the geometry of visual objects.
    Return MyBase.HitTestCore(hitTestParameters)
End Function

Siehe auch