Test trafienia w warstwie Visual

Ten temat zawiera omówienie funkcji testowania trafień udostępnianych przez warstwę wizualną. Obsługa testowania trafień pozwala określić, czy geometria lub wartość punktu mieści się w renderowanej zawartości Visualobiektu , co pozwala zaimplementować zachowanie interfejsu użytkownika, takie jak prostokąt zaznaczenia, aby wybrać wiele obiektów.

Scenariusze testowania trafień

Klasa UIElement udostępnia metodę InputHitTest , która umożliwia trafienie testu względem elementu przy użyciu danej wartości współrzędnych. W wielu przypadkach InputHitTest metoda zapewnia wymaganą funkcjonalność do implementowania testów trafień elementów. Istnieje jednak kilka scenariuszy, w których może być konieczne zaimplementowanie testowania trafień w warstwie wizualnej.

  • Testowanie trafień względem obiektów innych niżUIElement : dotyczy to testowania obiektów innychUIElement niż obiekty, takie jak DrawingVisual lub obiekty graficzne.

  • Testowanie trafień przy użyciu geometrii: dotyczy to sytuacji, w której należy wykonać test przy użyciu obiektu geometrii, a nie wartości współrzędnej punktu.

  • Testowanie trafień względem wielu obiektów: ma to zastosowanie w przypadku konieczności trafienia testu względem wielu obiektów, takich jak nakładające się obiekty. Możesz uzyskać wyniki dla wszystkich wizualizacji przecinających geometrię lub punkt, a nie tylko pierwszy.

  • Ignorowanie UIElement zasad testowania trafień: ma to zastosowanie, gdy trzeba zignorować UIElement zasady testowania trafień, które uwzględniają takie czynniki, jak wyłączenie lub niewidoczne elementu.

Uwaga

Aby zapoznać się z kompletnym przykładem kodu ilustrującym testowanie trafień w warstwie wizualnej, zobacz Hit Test using DrawingVisuals Sample (Test trafienia przy użyciu przykładu DrawingVisuals) i Hit Test with Win32 Interoperation Sample (Test trafienia przy użyciu przykładu RysunekVisuals) i Hit Test with Win32 Interoperation Sample (Test trafienia przy użyciu przykładu Win32 Interoperation).

Obsługa testowania trafień

HitTest Celem metod w VisualTreeHelper klasie jest określenie, czy wartość współrzędnych geometrii lub punktu znajduje się w renderowanej zawartości danego obiektu, takiej jak kontrolka lub element graficzny. Na przykład można użyć testowania trafień, aby określić, czy kliknięcie myszy w prostokątze ograniczenia obiektu mieści się w geometrii okręgu. Możesz również zastąpić domyślną implementację testowania trafień w celu wykonania własnych niestandardowych obliczeń testów trafień.

Na poniższej ilustracji przedstawiono relację między regionem obiektu nie prostokątnego a jego prostokątem ograniczenia.

Diagram of valid hit test region
Diagram prawidłowego regionu testu trafienia

Testowanie trafień i kolejność Z

Warstwa wizualizacji Windows Presentation Foundation (WPF) obsługuje testowanie trafień względem wszystkich obiektów w punkcie lub geometrii, a nie tylko najwięcej obiektów. Wyniki są zwracane w kolejności z. Jednak obiekt wizualizacji przekazywany jako parametr do HitTest metody określa, która część drzewa wizualnego zostanie trafiona. Możesz przejść test na całe drzewo wizualne lub dowolną jego część.

Na poniższej ilustracji obiekt okręgu znajduje się na górze zarówno obiektów kwadratowych, jak i trójkątnych. Jeśli interesuje Cię tylko testowanie obiektu wizualizacji, którego wartość z-order jest największa, możesz ustawić wyliczenie testu trafień wizualizacji, aby powrócić Stop z HitTestResultCallback elementu , aby zatrzymać przechodzenie testu trafienia po pierwszym elemencie.

Diagram of the z-order of a visual tree
Diagram kolejności z drzewa wizualnego

Jeśli chcesz wyliczyć wszystkie obiekty wizualne w określonym punkcie lub geometrii, wróć Continue z obiektu HitTestResultCallback. Oznacza to, że można trafić test dla obiektów wizualnych znajdujących się pod innymi obiektami, nawet jeśli są całkowicie zasłonięte. Aby uzyskać więcej informacji, zobacz przykładowy kod w sekcji "Używanie wywołania zwrotnego wyników testu trafienia".

Uwaga

Obiekt wizualny, który jest przezroczysty, może być również test trafiony.

Korzystanie z domyślnego testowania trafień

Można określić, czy punkt znajduje się w geometrii obiektu wizualizacji, używając HitTest metody w celu określenia obiektu wizualnego i wartości współrzędnej punktu do przetestowania. Parametr obiektu wizualizacji identyfikuje punkt początkowy w drzewie wizualnym wyszukiwania testów trafień. Jeśli obiekt wizualizacji znajduje się w drzewie wizualnym, którego geometria zawiera współrzędną, jest ustawiona na VisualHit właściwość HitTestResult obiektu. Element HitTestResult jest następnie zwracany z HitTest metody . Jeśli punkt nie jest zawarty w drzewie podrzędnym wizualizacji, zostanie osiągnięty test, HitTest zwraca wartość null.

Uwaga

Domyślne testowanie trafień zawsze zwraca najbardziej górny obiekt w kolejności z. Aby zidentyfikować wszystkie obiekty wizualne, nawet te, które mogą być częściowo lub całkowicie zasłonięte, użyj wywołania zwrotnego wyniku testu trafienia.

Wartość współrzędnych przekazywana jako parametr punktu dla HitTest metody musi być względna względem współrzędnej przestrzeni obiektu wizualizacji, względem którego przeprowadzasz testy. Jeśli na przykład obiekty wizualne zagnieżdżone są zdefiniowane na (100, 100) w przestrzeni współrzędnej elementu nadrzędnego, a następnie trafienie testujące wizualizację podrzędną na wartość (0, 0) jest równoważne testowaniu na poziomie (100, 100) w przestrzeni współrzędnej elementu nadrzędnego.

Poniższy kod pokazuje, jak skonfigurować programy obsługi zdarzeń myszy dla UIElement obiektu używanego do przechwytywania zdarzeń używanych do testowania trafień.

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

Wpływ drzewa wizualnego na testowanie trafień

Punkt początkowy w drzewie wizualizacji określa, które obiekty są zwracane podczas wyliczania testów trafień obiektów. Jeśli masz wiele obiektów, które chcesz wykonać test, obiekt wizualizacji używany jako punkt początkowy w drzewie wizualizacji musi być wspólnym elementem nadrzędnym wszystkich interesujących obiektów. Jeśli na przykład interesuje Cię testowanie zarówno elementu przycisku, jak i wizualizacja rysunkowa na poniższym diagramie, musisz ustawić punkt początkowy w drzewie wizualnym na wspólny obiekt nadrzędny obu tych elementów. W tym przypadku element kanwy jest typowym elementem nadrzędnym zarówno elementu przycisku, jak i wizualizacji rysunku.

Diagram of a visual tree hierarchy
Diagram hierarchii drzewa wizualnego

Uwaga

Właściwość IsHitTestVisible pobiera lub ustawia wartość, która deklaruje, czy UIElementobiekt pochodny może zostać zwrócony jako wynik testu trafienia z jakiejś części renderowanej zawartości. Dzięki temu można selektywnie zmienić drzewo wizualne w celu określenia, które obiekty wizualne są zaangażowane w test trafienia.

Używanie wywołania zwrotnego wyników testu trafienia

Można wyliczyć wszystkie obiekty wizualne w drzewie wizualnym, którego geometria zawiera określoną wartość współrzędnych. Dzięki temu można zidentyfikować wszystkie obiekty wizualne, nawet te, które mogą być częściowo lub całkowicie zasłonięte przez inne obiekty wizualne. Aby wyliczyć obiekty wizualne w drzewie wizualnym, użyj HitTest metody z funkcją wywołania zwrotnego testu trafienia. Funkcja wywołania zwrotnego testu trafienia jest wywoływana przez system, gdy określona wartość współrzędnych znajduje się w obiekcie wizualizacji.

Podczas wyliczania wyników testu trafień nie należy wykonywać żadnej operacji modyfikujące drzewo wizualne. Dodawanie lub usuwanie obiektu z drzewa wizualnego podczas przechodzenia może spowodować nieprzewidywalne zachowanie. Drzewo wizualne można bezpiecznie zmodyfikować po HitTest powrocie metody. Możesz podać strukturę danych, taką jak , ArrayListdo przechowywania wartości podczas wyliczania wyników testu trafień.

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

Metoda wywołania zwrotnego testu trafienia definiuje akcje wykonywane po zidentyfikowaniu testu trafienia na określonym obiekcie wizualnym w drzewie wizualnym. Po wykonaniu akcji zwracana HitTestResultBehavior jest wartość określająca, czy kontynuować wyliczanie innych obiektów wizualnych, czy nie.

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

Uwaga

Kolejność wyliczania trafień obiektów wizualnych jest według kolejności z. Obiekt wizualizacji na najwyższym poziomie kolejności z jest pierwszym wyliczonym obiektem. Wszystkie inne obiekty wizualne, które zostały wyliczone, są zmniejszane na poziomie z-order. Ta kolejność wyliczenia odpowiada kolejności renderowania wizualizacji.

Możesz zatrzymać wyliczanie obiektów wizualnych w dowolnym momencie w funkcji wywołania zwrotnego testu trafienia Stop, zwracając polecenie .

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

Używanie wywołania zwrotnego filtru testu trafienia

Możesz użyć opcjonalnego filtru testu trafienia, aby ograniczyć obiekty przekazywane do wyników testu trafienia. Dzięki temu można ignorować części drzewa wizualnego, które nie są zainteresowane przetwarzaniem w wynikach testu trafień. Aby zaimplementować filtr testu trafienia, należy zdefiniować funkcję wywołania zwrotnego filtru testu trafienia i przekazać ją jako wartość parametru podczas wywoływania HitTest metody.

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

Jeśli nie chcesz podać opcjonalnej funkcji wywołania zwrotnego filtru testu trafienia, przekaż null wartość jako parametr dla HitTest metody .

// 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
Oczyszczanie drzewa wizualnego

Funkcja wywołania zwrotnego filtru trafień umożliwia wyliczanie wszystkich wizualizacji, których renderowana zawartość zawiera określone współrzędne. Możesz jednak zignorować niektóre gałęzie drzewa wizualnego, które nie są zainteresowane przetwarzaniem funkcji wywołania zwrotnego wyników testu trafienia. Zwracana wartość funkcji wywołania zwrotnego filtru testu trafienia określa typ akcji, jaką należy wykonać wyliczenie obiektów wizualizacji. Jeśli na przykład zwrócisz wartość , ContinueSkipSelfAndChildrenmożesz usunąć bieżący obiekt wizualizacji i jego elementy podrzędne z wyliczenia wyników testu trafień. Oznacza to, że funkcja wywołania zwrotnego wyników testu trafień nie będzie widzieć tych obiektów w wyliczeniu. Przycinanie drzewa wizualnego obiektów zmniejsza ilość przetwarzania podczas wyliczenia wyników testu trafienia. W poniższym przykładzie kodu filtr pomija etykiety i ich elementy podrzędne i sprawdza wszystkie inne elementy podrzędne.

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

Uwaga

Wywołanie zwrotne filtru testu trafienia będzie czasami wywoływane w przypadkach, w których wywołanie zwrotne wyników testu trafienia nie jest wywoływane.

Zastępowanie domyślnego testowania trafień

Można zastąpić domyślną obsługę testowania trafień obiektu wizualizacji, przesłaniając metodę HitTestCore . Oznacza to, że podczas wywoływania HitTest metody wywoływana jest zastępowana implementacja HitTestCore metody . Metoda przesłonięta jest wywoływana, gdy test trafienia mieści się w obrębie prostokąta ograniczenia obiektu wizualizacji, nawet jeśli współrzędna znajduje się poza renderowaną zawartością obiektu wizualizacji.

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

Może wystąpić czas trafienia testu zarówno względem prostokąta ograniczenia, jak i renderowanej zawartości obiektu wizualizacji. Używając wartości parametru PointHitTestParameters w metodzie przesłoniętej HitTestCore jako parametru do metody HitTestCorepodstawowej , można wykonywać akcje na podstawie trafienia prostokąta ograniczenia obiektu wizualizacji, a następnie wykonać drugi test trafienia względem renderowanej zawartości obiektu wizualizacji.

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

Zobacz też