Hit testing a livello visivo

In questo argomento vengono forniti dei cenni preliminari sulle funzionalità di hit testing fornite dal livello visivo. Il supporto per l'hit testing consente di determinare se il valore di una geometria o di un punto è compreso all'interno del contenuto di rendering di un oggetto Visual, rendendo possibile l'implementazione di un comportamento dell'interfaccia utente, ad esempio un rettangolo per la selezione di più oggetti.

Nel presente argomento sono contenute le seguenti sezioni.

  • Scenari di hit testing
  • Supporto per l'hit testing
  • Hit testing e ordine Z
  • Utilizzo dell'hit testing predefinito
  • Utilizzo del callback dei risultati di un hit test
  • Utilizzo del callback di un filtro dell'hit test
  • Override dell'hit testing predefinito
  • Argomenti correlati

Scenari di hit testing

La classe UIElement fornisce il metodo InputHitTest che consente di eseguire un hit test su un elemento utilizzando un valore di coordinate specificato. In molti casi, il metodo InputHitTest fornisce le funzionalità richieste per l'implementazione dell'hit testing degli elementi. Esistono tuttavia diversi scenari nei quali potrebbe essere necessario implementare l'hit testing a livello visivo.

  • Hit testing su oggetti diversi da UIElement: applicabile se si esegue l'hit test su oggetti che non sono UIElement, ad esempio DrawingVisual oppure oggetti grafici.

  • Hit testing tramite una geometria: applicabile quando è necessario eseguire un hit test utilizzando un oggetto Geometry anziché il valore delle coordinate di un punto.

  • Hit testing su più oggetti: applicabile quando è necessario eseguire un hit test su più oggetti, ad esempio oggetti sovrapposti. È possibile ottenere risultati per tutti gli elementi visivi che intersecano una geometria o un punto, non solo il primo di essi.

  • Criteri di hit testing di oggetti UIElement ignorati: applicabile quando è necessario ignorare i criteri di hit testing di oggetti UIElement, che prendono in considerazione fattori quali l'eventuale disabilitazione o invisibilità di un elemento.

NotaNota

Per un esempio di codice completo che illustri l'hit testing a livello visivo, vedere Esempio di hit test mediante DrawingVisual e Esempio di hit test con interoperatività Win32 (le pagine potrebbero essere in inglese).

Supporto per l'hit testing

Lo scopo dei metodi HitTest della classe VisualTreeHelper consiste nel determinare se il valore delle coordinate di un punto o di una geometria è compreso all'interno del contenuto di rendering di un determinato oggetto, ad esempio un controllo o un elemento grafico. Ad esempio, è possibile utilizzare l'hit testing per determinare se un clic del mouse all'interno del rettangolo di delimitazione di un oggetto viene eseguito all'interno della geometria di un cerchio. Inoltre, è possibile scegliere di sottoporre a override l'implementazione predefinita dell'hit testing per eseguire calcoli di hit testing personalizzati.

Nell'illustrazione seguente viene mostrata la relazione tra l'area di un oggetto non rettangolare e il relativo rettangolo di delimitazione.

Diagramma dell'area di hit testing valida

Diagramma dell'area di hit testing valida

Hit testing e ordine Z

Il livello visivo di Windows Presentation Foundation (WPF) supporta l'hit testing su tutti gli oggetti relativi a un punto oppure a una geometria, non solo sull'oggetto di primo livello. I risultati vengono restituiti nell'ordine Z. Tuttavia, l'oggetto visivo che viene passato come parametro al metodo HitTest determina la porzione della struttura ad albero visuale che sarà sottoposta all'hit testing. È possibile eseguire l'hit testing sull'intera struttura ad albero visuale o su qualsiasi parte di essa.

Nell'illustrazione seguente, l'oggetto cerchio è sovrapposto sia al quadrato che al triangolo. Se si desidera sottoporre a hit testing solo l'oggetto visivo il cui valore dell'ordine Z corrisponde al livello superiore, è possibile impostare l'enumerazione dell'hit test visiva in modo che l'oggetto HitTestResultCallback restituisca Stop per interrompere lo scorrimento dell'hit test dopo il primo elemento.

Diagramma dell'ordine Z di una struttura ad albero visuale

Diagramma dell'ordine Z di una struttura ad albero visuale

Se si desidera enumerare tutti gli oggetti visivi in un punto o in una geometria specifica, è necessario che l'oggetto HitTestResultCallback restituisca Continue. Ciò significa che è possibile sottoporre a hit testing oggetti visivi che si trovano al di sotto di altri oggetti, anche se sono completamente nascosti. Per ulteriori informazioni, vedere il codice di esempio nella sezione "Utilizzo del callback dei risultati di un hit test".

NotaNota

È possibile eseguire l'hit test anche su un oggetto visivo trasparente.

Utilizzo dell'hit testing predefinito

È possibile individuare se un punto è compreso all'interno della geometria di un oggetto visivo utilizzando il metodo HitTest per specificare un oggetto visivo e il valore delle coordinate di un punto rispetto ai quali eseguire il test. Il parametro dell'oggetto visivo identifica il punto di avvio della ricerca dell'hit test nella struttura ad albero visuale. Se viene rilevato un oggetto visivo nella struttura ad albero visuale la cui geometria contiene la coordinata, questo sarà impostato sulla proprietà VisualHit di un oggetto HitTestResult. L'oggetto HitTestResult viene quindi restituito dal metodo HitTest. Se il punto non è contenuto all'interno della sottostruttura ad albero visuale sottoposta a hit testing, il metodo HitTest restituisce null.

NotaNota

L'hit testing predefinito restituisce sempre l'oggetto di livello superiore nell'ordine Z.Per identificare tutti gli oggetti visivi, compresi quelli che potrebbero essere parzialmente o completamente nascosti, utilizzare un callback dei risultati dell'hit test.

Il valore delle coordinate che viene passato come parametro del punto per il metodo HitTest deve essere relativo allo spazio delle coordinate dell'oggetto visivo sottoposto a hit testing. Ad esempio, se sono stati annidati degli oggetti visivi definiti in corrispondenza delle coordinate (100, 100) nello spazio di coordinate dell'elemento padre, l'hit testing su un oggetto visivo figlio di coordinate (0, 0) sarà equivalente all'hit testing per le coordinate (100, 100) nello spazio di coordinate dell'elemento padre.

Nel codice seguente viene mostrato come configurare i gestori di eventi del mouse per un oggetto UIElement utilizzato per acquisire gli eventi adoperati per l'hit testing.

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

Effetti della struttura ad albero visuale sull'hit testing

Il punto iniziale della struttura ad albero visuale determina gli oggetti che saranno restituiti durante l'enumerazione degli oggetti dell'hit test. Se si desidera sottoporre a hit testing più oggetti, l'oggetto visivo utilizzato come punto iniziale della struttura ad albero visuale deve essere il predecessore comune di tutti gli oggetti in questione. Se, ad esempio, si desidera eseguire l'hit test sia sull'elemento pulsante, sia sull'elemento visivo di disegno del diagramma riportato di seguito, è necessario impostare il punto iniziale della struttura ad albero visuale in corrispondenza del predecessore comune a entrambi. In questo caso, l'elemento area di disegno è il predecessore comune per l'elemento pulsante e per l'elemento visivo di disegno.

Diagramma della gerarchia di una struttura ad albero visuale

Diagramma della gerarchia di una struttura ad albero visuale

NotaNota

La proprietà IsHitTestVisible ottiene o imposta un valore che dichiara se un oggetto derivato da UIElement può eventualmente essere restituito come risultato dell'hit test da qualche porzione del relativo contenuto di rendering.In questo modo è possibile modificare selettivamente la struttura ad albero visuale per determinare quali oggetti visivi sono coinvolti in un hit test.

Utilizzo del callback dei risultati di un hit test

È possibile enumerare tutti gli oggetti visivi di una struttura ad albero visuale la cui geometria contiene un valore di coordinate specificato. Ciò consente di identificare tutti gli oggetti visivi, compresi quelli che potrebbero essere parzialmente o completamente nascosti da altri oggetti visivi. Per enumerare gli oggetti visivi di una struttura ad albero visuale, utilizzare il metodo HitTest con una funzione di callback dell'hit test. La funzione di callback dell'hit test viene chiamata dal sistema quando il valore delle coordinate specificato è compreso in un oggetto visivo.

Durante l'enumerazione dei risultati dell'hit test, evitare di eseguire qualsiasi operazione che modifichi la struttura ad albero visuale. L'aggiunta o la rimozione di un oggetto dalla struttura ad albero visuale mentre quest'ultima viene attraversata può produrre un comportamento imprevedibile. È possibile modificare la struttura ad albero visuale senza correre rischi dopo la restituzione dei risultati da parte del metodo HitTest. È necessario fornire una struttura dei dati, ad esempio un oggetto ArrayList, per archiviare i valori durante l'enumerazione dei risultati dell'hit test.

        ' 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
// 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);
    }
}

Il metodo di callback dell'hit test definisce le azioni eseguite quando viene identificato un hit test su un determinato oggetto visivo nella struttura ad albero visuale. Una volta eseguite queste azioni, viene restituito un valore di HitTestResultBehavior che consente di determinare se continuare o meno l'enumerazione di altri eventuali oggetti visivi.

        ' 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
// 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;
}
NotaNota

La sequenza di enumerazione degli oggetti visivi sottoposti a hit testing è basata sull'ordine Z.L'oggetto visivo di livello superiore nell'ordine Z viene enumerato per primo.Gli altri oggetti visivi vengono enumerati secondo livelli decrescenti dell'ordine Z.Tale ordine di enumerazione corrisponde all'ordine di rendering degli elementi visivi.

È possibile interrompere l'enumerazione di oggetti visivi in qualsiasi momento nella funzione di callback dell'hit test restituendo Stop.

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

Utilizzo del callback di un filtro dell'hit test

È possibile utilizzare un filtro dell'hit test facoltativo per limitare gli oggetti passati ai risultati dell'hit test. Ciò consente di ignorare le parti della struttura ad albero visuale che non si desidera elaborare nei risultati dell'hit test. Per implementare un filtro dell'hit test, definire una funzione di callback del filtro dell'hit test e passarla come valore del parametro quando si chiama il metodo HitTest.

        ' 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
// 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();
    }
}

Se non si desidera specificare la funzione di callback del filtro dell'hit test facoltativo, passare un valore null come parametro per il metodo HitTest.

            ' 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.
// 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));

Eliminazione di oggetti da una struttura ad albero visuale

Eliminazione di una struttura ad albero visuale con un filtro hit test

La funzione di callback del filtro dell'hit test consente di enumerare tutti gli oggetti visivi il cui contenuto di rendering include le coordinate specificate. Tuttavia è possibile che talvolta si decida di ignorare determinate diramazioni che non si desidera elaborare nella funzione di callback dei risultati dell'hit test. Il valore restituito dalla funzione di richiamata del filtro dell'hit test determina il tipo di azione che deve essere eseguito dall'enumerazione degli oggetti visivi. Se, ad esempio, viene restituito il valore ContinueSkipSelfAndChildren, è possibile rimuovere l'oggetto visivo corrente e i relativi elementi figlio dall'enumerazione dei risultati dell'hit test. In questo modo, la funzione di callback dei risultati dell'hit test non visualizzerà tali oggetti nell'enumerazione. L'eliminazione di oggetti dalla struttura ad albero visuale consente di ridurre l'elaborazione durante il passaggio dell'enumerazione dei risultati dell'hit test. Nell'esempio di codice seguente, il filtro ignora le etichette e i relativi discendenti e sottopone a hit test tutti gli altri elementi.

        ' 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
// 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;
    }
}
NotaNota

Talvolta il callback del filtro dell'hit test sarà chiamato nei casi in cui non viene chiamato il callback dei risultati dell'hit test.

Override dell'hit testing predefinito

È possibile eseguire l'override del supporto dell'hit testing predefinito di un oggetto visivo tramite l'override del metodo HitTestCore. In questo modo, quando si richiama il metodo HitTest, viene chiamata l'implementazione di HitTestCore di cui è stato eseguito l'override. Il metodo sottoposto a override viene chiamato quando un hit test viene eseguito all'interno del rettangolo di delimitazione dell'oggetto visivo, anche se la coordinata non rientra nel contenuto di rendering dell'oggetto visivo.

        ' 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
// 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);
}

A volte può essere opportuno eseguire un hit test sia sul rettangolo di delimitazione, sia sul contenuto di rendering di un oggetto visivo. Utilizzando il valore del parametro PointHitTestParameters nel metodo HitTestCore sottoposto a override come parametro per il metodo di base HitTestCore, è possibile effettuare operazioni basate su un'occorrenza del rettangolo di delimitazione di un oggetto visivo ed eseguire quindi un secondo hit test sul contenuto sottoposto a rendering dell'oggetto visivo.

        ' 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
// 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);
}

Vedere anche

Attività

Procedura: eseguire un hit test della geometria in un oggetto Visual

Procedura: eseguire un hit test utilizzando un contenitore di host Win32

Riferimenti

HitTest

HitTestResult

HitTestResultCallback

HitTestFilterCallback

IsHitTestVisible

Altre risorse

Esempio di hit test mediante DrawingVisuals

Esempio di hit test con interoperatività Win32