Testowanie trafień w warstwie wizualnej
Ten temat zawiera omówienie funkcji testowania trafień udostępnianych przez warstwę wizualną. Obsługa testowania trafień pozwala określić, czy geometria lub punkt mieści się w renderowanej zawartości Visual, co pozwala wprowadzić w interfejsie użytkownika funkcjonalność taką 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 metoda InputHitTest zapewnia pożądaną funkcjonalność do przeprowadzania testów kolizji 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 sytuacji, gdy testujesz obiekty inne niżUIElement, 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, gdy trzeba testować trafienia dla wielu obiektów, na przykład nakładających się. Możesz uzyskać wyniki dla wszystkich wizualizacji przecinających geometrię lub punkt, a nie tylko pierwszy.
Ignorowanie zasad testowania trafień UIElement: ma to zastosowanie, gdy trzeba zignorować zasady testowania trafień UIElement, które uwzględniają takie czynniki, jak to, czy element jest wyłączony lub niewidoczny.
Notatka
Aby uzyskać kompletny przykład kodu ilustrujący testowanie trafień w warstwie wizualizacji, zobacz przykład testowania trafienia z użyciem DrawingVisuals oraz przykład interoperacyjności z Win32.
Obsługa testowania trafień
Celem metod HitTest w klasie VisualTreeHelper 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ącie otaczającym obiekt znajduje się w geometrii okręgu. Możesz również zastąpić domyślną implementację testowania trafień, aby przeprowadzić własne niestandardowe obliczenia do testów trafień.
Na poniższej ilustracji przedstawiono relację między regionem obiektu nie prostokątnego a jego prostokątem ograniczenia.
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 obiektu najbardziej powierzchniowego. Wyniki są zwracane w porządku typu z. Jednak obiekt wizualizacji przekazywany jako parametr do metody HitTest określa, która część drzewa wizualnego będzie testowa. 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 trafień obiektu wizualnego, który znajduje się na najwyższym poziomie w kolejności z-warstw, możesz ustawić enumerację testu trafień wizualnych, aby zwrócić Stop z HitTestResultCallback, co zatrzymuje przechodzenie testu po pierwszym elemencie.
Diagram porządku warstwowego w drzewie wizualnym
Jeśli chcesz wyliczyć wszystkie obiekty wizualne w określonym punkcie lub geometrii, zwróć Continue z HitTestResultCallback. Oznacza to, że można przeprowadzić test trafienia dla obiektów wizualnych znajdujących się pod innymi obiektami, nawet jeśli są całkowicie zasłonięte. Aby dowiedzieć się więcej, zobacz przykładowy kod w sekcji "Używanie wywołania zwrotnego wyników testu trafienia".
Notatka
Obiekt wizualny, który jest przezroczysty, można również poddać testowi kolizji.
Korzystanie z domyślnego testowania trafień
Można określić, czy punkt znajduje się w geometrii obiektu wizualizacji, używając metody HitTest 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 w drzewie wizualnym zostanie znaleziony obiekt wizualny, którego geometria zawiera współrzędną, zostaje on ustawiony jako właściwość VisualHit obiektu HitTestResult. Następnie HitTestResult jest zwracana z metody HitTest. Jeśli punkt nie jest zawarty w testowanym drzewie podrzędnym wizualizacji, HitTest zwraca null
.
Notatka
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 metody HitTest musi być względna względem współrzędnej przestrzeni obiektu wizualizacji, względem którego przeprowadzasz testy. Na przykład, jeśli obiekty wizualne są zagnieżdżone i zdefiniowane na współrzędnych (100, 100) w przestrzeni współrzędnych elementu nadrzędnego, to testowanie trafień wizualizacji elementu podrzędnego na współrzędnych (0, 0) jest równoważne testowaniu trafień na współrzędnych (100, 100) w przestrzeni współrzędnych elementu nadrzędnego.
Poniższy kod pokazuje, jak skonfigurować programy obsługi zdarzeń myszy dla obiektu UIElement 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 kolizji
Punkt początkowy w drzewie wizualnym określa, które obiekty są zwracane podczas przeprowadzania testu trafienia na obiekty. 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 wizualizacji rysunkowej na poniższym diagramie, musisz ustawić punkt początkowy w drzewie wizualizacji na wspólny obiekt nadrzędny obu tych elementów. W tym przypadku element kanwy jest wspólnym przodkiem zarówno przycisku, jak i elementu wizualnego rysunku.
Diagram hierarchii drzewa wizualnego
Notatka
Właściwość IsHitTestVisible pobiera lub ustawia wartość, która deklaruje, czy obiekt pochodny UIElementmoże zostać zwrócony jako wynik testu trafienia z części renderowanej zawartości. Dzięki temu można selektywnie modyfikować drzewo wizualne, aby określić, które obiekty wizualne są zaangażowane w test trafienia.
Używanie wywołania zwrotnego wyników testu kolizji
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 wizualizacji, użyj metody HitTest 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ących drzewo wizualne. Dodawanie lub usuwanie obiektu z drzewa wizualnego podczas przechodzenia może spowodować nieprzewidywalne zachowanie. Drzewo wizualne można bezpiecznie zmodyfikować po powrocie metody HitTest. Możesz podać strukturę danych, taką jak ArrayList, do 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 zwracasz wartość HitTestResultBehavior, która określa, 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
Nota
Kolejność wyliczania trafień obiektów wizualnych jest według kolejności z. Obiekt wizualny w najwyższym poziomie z-indexu jest pierwszym obiektem, który jest enumerowany. Wszystkie inne obiekty wizualne, które zostały wyliczone, znajdują się na malejących poziomach warstwy Z. Ta kolejność wyliczenia odpowiada kolejności renderowania wizualizacji.
Możesz zatrzymać wyliczanie obiektów wizualnych w dowolnym momencie w funkcji testu trafienia wywołania zwrotnego, zwracając Stop.
// 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ą istotne do przetwarzania przy sprawdzaniu 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 metody 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
Jeśli nie chcesz podać opcjonalnej funkcji wywołania zwrotnego filtru testu trafienia, przekaż wartość null
jako parametr metody 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.
Przycinanie drzewa wizualnego
Funkcja wywołania zwrotnego filtra testu trafień pozwala przeglądać wszystkie elementy wizualne, których renderowana zawartość zawiera współrzędne, które określisz. Możesz jednak zignorować niektóre gałęzie drzewa wizualnego, które nie chcesz przetwarzać w 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ść, ContinueSkipSelfAndChildren, możesz usunąć bieżący obiekt wizualizacji i jego elementy podrzędne z wyliczenia wyników testu trafienia. 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
Notatka
Wywołanie zwrotne filtru testu trafienia niekiedy nastąpi w przypadkach, w których wywołanie zwrotne wyników testu trafienia nie jest wykonywane.
Zastępowanie domyślnego testowania trafień
Można nadpisać domyślną obsługę wykrywania trafień obiektu wizualnego, przesłaniając metodę HitTestCore. Oznacza to, że kiedy wywołujesz metodę HitTest, wywoływana jest twoja zastąpiona implementacja HitTestCore. Twoja metoda przesłonięta jest wywoływana, gdy test trafienia mieści się w granicach prostokąta ograniczającego obiekt wizualny, nawet jeśli współrzędna znajduje się poza renderowaną zawartością tego obiektu.
// 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 się zdarzyć, że chcesz przeprowadzić test trafienia zarówno względem prostokąta ograniczającego, jak i renderowanej treści obiektu wizualnego. Używając wartości parametru PointHitTestParameters
w przesłoniętej metodzie HitTestCore jako parametru metody podstawowej HitTestCore, można wykonywać akcje na podstawie trafienia w prostokąt obwiedni obiektu wizualnego, a następnie przeprowadzić drugi test trafienia względem renderowanej zawartości tego obiektu wizualnego.
// 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ż
.NET Desktop feedback