Dela via


Tryck på Testning i det visuella lagret

Det här avsnittet innehåller en översikt över funktionen för träfftestning som tillhandahålls av det visuella lagret. Med träfftestningens stöd kan du avgöra om en geometrisk form eller en punkt faller inom det renderade innehållet i ett Visual, så att du kan implementera användargränssnittsbeteenden, till exempel en markeringsrektangel för att välja flera objekt.

Scenarier för träfftestning

Klassen UIElement innehåller metoden InputHitTest, som gör att du kan köra test mot ett element med ett visst koordinatvärde. I många fall ger metoden InputHitTest önskade funktioner för att implementera träfftestning av element. Det finns dock flera scenarier där du kan behöva implementera träfftestning på det visuella lagret.

  • Träfftest mot objekt som inte ärUIElement: Detta gäller om du träfftestar icke-UIElement-objekt, till exempel DrawingVisual- eller grafikobjekt.

  • Träfftestning med hjälp av en geometri: Detta gäller om du behöver göra ett test med hjälp av ett geometriobjekt i stället för koordinatvärdet för en punkt.

  • Träfftestning mot flera objekt: Detta gäller när du behöver testa mot flera objekt, till exempel överlappande objekt. Du kan få resultat för alla visuella objekt som korsar en geometri eller punkt, inte bara den första.

  • Ignorera UIElement träfftestningspolicy: Detta gäller när du behöver ignorera UIElement träfftestningspolicy, som tar hänsyn till faktorer som om ett element är inaktiverat eller osynligt.

Obs

Ett komplett kodexempel som illustrerar träfftestning på det visuella lagret finns i Träfftest med DrawingVisuals-exempel och Träfftest med Win32-interoperabilitetsexempel.

Stöd för träfftestning

Syftet med HitTest metoder i klassen VisualTreeHelper är att avgöra om ett geometri- eller punktkoordinatvärde ligger inom det renderade innehållet i ett visst objekt, till exempel ett kontroll- eller grafiskt element. Du kan till exempel använda träfftestning för att avgöra om ett musklick inom avgränsningsrektangeln för ett objekt faller inom en cirkels geometri. Du kan också välja att åsidosätta standardimplementeringen av träfftestning för att utföra dina egna anpassade beräkningar för träfftest.

Följande bild visar relationen mellan ett icke-rektangulärt objekts region och dess avgränsningsrektangel.

Diagram över giltig träfftestregion
Diagram över giltig träfftestregion

Träfftestning och Z-order

Det visuella lagret i Windows Presentation Foundation (WPF) stöder träfftestning mot alla objekt under en punkt eller geometri, inte bara det översta objektet. Resultaten returneras i z-ordning. Det visuella objekt som du skickar som parameter till metoden HitTest avgör dock vilken del av det visuella trädet som ska träffa testet. Du kan göra ett test mot hela det visuella trädet eller mot vilken del som helst av det.

I följande bild är cirkelobjektet ovanpå både kvadrat- och triangelobjekten. Om du bara är intresserad av att testa det visuella objektet vars z-order-värde är högst, kan du ange att den visuella träfftestuppräkningen ska returnera Stop från HitTestResultCallback för att stoppa träfftestets bläddring efter det första objektet.

diagram över z-ordningen för ett visuellt träd
Diagram över z-ordningen för ett visuellt träd

Om du vill räkna upp alla visuella objekt under en viss punkt eller geometri returnerar du Continue från HitTestResultCallback. Det innebär att du kan testa visuella objekt som finns under andra objekt, även om de är helt dolda. Mer information finns i exempelkoden i avsnittet "Använda ett återanrop till träfftestresultat".

Note

Ett visuellt objekt som är transparent kan också testas.

Använda standard träfftestning

Du kan identifiera om en punkt ligger inom geometrin för ett visuellt objekt genom att använda metoden HitTest för att ange ett visuellt objekt och ett punktkoordinatvärde att testa mot. Den visuella objektparametern identifierar startpunkten i det visuella trädet för träfftestsökningen. Om ett visuellt objekt hittas i det visuella trädet vars geometri innehåller koordinaten anges det till egenskapen VisualHit för ett HitTestResult objekt. HitTestResult returneras sedan från metoden HitTest. Om punkten inte ingår i det visuella underträdet som du träffar på testningen returnerar HitTestnull.

Notera

Standardtestning av träff returnerar alltid det översta objektet i z-ordningen. För att identifiera alla visuella objekt, även de som kan vara delvis eller helt dolda, använder du ett återanrop till träfftestresultatet.

Koordinatvärdet som du skickar som punktparameter för metoden HitTest måste vara relativt koordinatutrymmet för det visuella objekt som du stöter på testningen mot. Om du till exempel har kapslade visuella objekt som definierats på (100, 100) i det överordnade koordinatutrymmet, så motsvarar att träfftesta ett underordnat visuellt objekt på (0, 0) att träfftesta vid (100, 100) i det överordnade koordinatutrymmet.

Följande kod visar hur du konfigurerar mushändelsehanterare för ett UIElement objekt som används för att avbilda händelser som används för träfftestning.

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

Hur det visuella trädet påverkar träfftestning

Startpunkten i det visuella trädet avgör vilka objekt som returneras under träfftestuppräkning av objekt. Om du har flera objekt som du vill testa måste det visuella objektet som används som startpunkt i det visuella trädet vara den gemensamma förfadern till alla objekt av intresse. Om du till exempel var intresserad av att testa både knappelementet och ritningens visuella element i följande diagram måste du ange startpunkten i det visuella trädet till den gemensamma anfader för båda. I det här fallet är canvaselementet den gemensamma förfadern för både knappelementet och det visuella ritobjektet.

diagram över en visuell trädhierarki
Diagram över en visuell trädhierarki

Not

Egenskapen IsHitTestVisible hämtar eller anger ett värde som anger om ett UIElement-härlett objekt kan returneras som ett träfftestresultat från någon del av dess renderade innehåll. På så sätt kan du selektivt ändra det visuella trädet för att avgöra vilka visuella objekt som ingår i ett träfftest.

Använda ett träfftestresultatsåteranrop

Du kan räkna upp alla visuella objekt i ett visuellt träd vars geometri innehåller ett angivet koordinatvärde. På så sätt kan du identifiera alla visuella objekt, även de som delvis eller helt döljs av andra visuella objekt. Om du vill räkna upp visuella objekt i ett visuellt träd använder du metoden HitTest med en återanropsfunktion för träfftest. Återanropsfunktionen för träfftest anropas av systemet när det koordinatvärde som du anger finns i ett visuellt objekt.

Under uppräkningen av träfftestresultat bör du inte utföra någon åtgärd som ändrar det visuella trädet. Om du lägger till eller tar bort ett objekt från det visuella trädet medan det bläddras kan det leda till oförutsägbart beteende. Du kan på ett säkert sätt ändra det visuella trädet när metoden HitTest returneras. Du kanske vill ange en datastruktur, till exempel en ArrayList, för att lagra värden under träfftestresultatuppräkningen.

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

Återanropsmetoden för träfftest definierar de åtgärder som du utför när ett träfftest identifieras på ett visst visuellt objekt i det visuella trädet. När du har slutfört åtgärderna returnerar du ett HitTestResultBehavior värde som avgör om uppräkningen av andra visuella objekt ska fortsätta eller inte.

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

Not

Uppräkningsordningen för visuella träffobjekt är enligt z-ordning. Det visuella objektet på den översta z-ordernivån är det första objekt som räknas upp. Alla andra visuella objekt som räknas upp är på fallande z-ordernivå. Den här uppräkningsordningen motsvarar återgivningsordningen för de visuella objekten.

Du kan stoppa uppräkningen av visuella objekt när som helst i återanropsfunktionen för träfftest genom att returnera Stop.

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

Använda ett återanrop för träfftestfilter

Du kan använda ett valfritt träfftestfilter för att begränsa de objekt som skickas till träfftestresultaten. På så sätt kan du ignorera delar av det visuella trädet som du inte är intresserad av att bearbeta i dina träfftestresultat. Om du vill implementera ett träfftestfilter definierar du en återanropsfunktion för träfftestfilter och skickar den som ett parametervärde när du anropar metoden HitTest.

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

Om du inte vill ange den valfria återanropsfunktionen för träfftestfiltret skickar du ett värde på null som parameter för metoden HitTest.

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

rensa ett visuellt träd med hjälp av ett testfilter
Beskära ett visuellt träd

Med återanropsfunktionen för träfftestfiltret kan du gå igenom alla visuella objekt vars innehåll innehåller de koordinater du anger. Men du kanske vill ignorera vissa grenar av det visuella trädet som du inte är intresserad av att bearbeta i återanropsfunktionen för träfftestresultat. Returvärdet för callback-funktionen för träfftestfiltret bestämmer vilken typ av åtgärd som bör vidtas vid uppräkningen av de visuella objekten. Om du till exempel returnerar värdet ContinueSkipSelfAndChildrenkan du ta bort det aktuella visuella objektet och dess underordnade objekt från uppräkningen av träfftestresultatet. Det innebär att återanropsfunktionen för träfftestresultat inte ser dessa objekt i uppräkningen. Om du rensar objektens visuella träd minskar bearbetningen under uppräkningspasset för träfftestresultatet. I följande kodexempel hoppar filtret över etiketter och deras underordnade och testar allt annat.

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

Not

Retur-anropet för träfftestfiltret anropas ibland i de fall där retur-anropet för träfftestresultatet inte anropas.

Åsidosätta standardinställning för träfftestning

Du kan åsidosätta ett visuellt objekts standardteststöd för träff genom att åsidosätta metoden HitTestCore. Det innebär att när du anropar metoden HitTest, anropas din överskrivna implementering av HitTestCore. Den åsidosatta metoden anropas när ett träfftest faller inom det visuella objektets avgränsningsrektangel, även om koordinaten faller utanför det renderade innehållet i det visuella objektet.

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

Det kan finnas tillfällen då du vill testa både avgränsningsrektangeln och det renderade innehållet i ett visuellt objekt. Genom att använda PointHitTestParameters parametervärdet i den åsidosättna HitTestCore-metoden som parameter till basmetoden HitTestCorekan du utföra åtgärder baserat på en träff av avgränsningsrektangeln för ett visuellt objekt och sedan utföra ett andra träfftest mot det renderade innehållet i det visuella objektet.

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

Se även