Teste de clique na camada visual

Este tópico fornece uma visão geral da funcionalidade de teste de clique fornecida pela camada visual. O suporte ao teste de ocorrências permite determinar se uma geometria ou um valor de ponto se enquadra no conteúdo renderizado de um , permitindo que você implemente o comportamento da interface do usuário, como um Visualretângulo de seleção para selecionar vários objetos.

Cenários de teste de clique

A UIElement classe fornece o método, que permite que você acerte o InputHitTest teste em um elemento usando um determinado valor de coordenada. Em muitos casos, o método fornece a funcionalidade desejada para implementar o InputHitTest teste de acerto de elementos. No entanto, há várias situações em que talvez seja necessário implementar o teste de clique na camada visual.

  • Teste de acerto contra não-objetos: Isso se aplica se você estiver testando objetos não-UIElementUIElement, como DrawingVisual objetos gráficos ou objetos.

  • Testes de clique usando uma geometria: isso se aplica se você precisa realizar teste de clique usando um objeto de geometria em vez do valor de coordenada de um ponto.

  • Testes de clique em vários objetos: isso se aplica quando você precisa realizar teste de clique em vários objetos, como objetos sobrepostos. Você pode obter resultados para todos os elementos visuais que interceptam uma geometria ou ponto e não apenas o primeiro deles.

  • Ignorando a política de teste de ocorrência: Isso se aplica quando você precisa ignorar a política de teste de UIElementUIElement ocorrência, que leva em consideração fatores como se um elemento está desabilitado ou invisível.

Observação

Para um exemplo de código completo ilustrando o teste de clique na camada visual, consulte Exemplo de teste de clique usando DrawingVisuals e Exemplo de teste de clique com interoperação do Win32.

Suporte de teste de clique

O objetivo dos HitTest métodos na VisualTreeHelper classe é determinar se uma geometria ou valor de coordenada de ponto está dentro do conteúdo renderizado de um determinado objeto, como um controle ou elemento gráfico. Por exemplo, você poderia usar o teste de clique para determinar se um clique do mouse, dentro do retângulo delimitador de um objeto, cai dentro da geometria de um círculo. Você também pode optar por substituir a implementação padrão de teste de clique para realizar seus próprios cálculos personalizados de teste de clique.

A ilustração a seguir mostra a relação entre a região não retangular de um objeto e seu retângulo delimitador.

Diagram of valid hit test region
Diagrama de região de teste de clique válida

Teste de clique e ordem z

A camada visual do Windows Presentation Foundation (WPF) oferece suporte ao teste de ocorrências em todos os objetos sob um ponto ou geometria, não apenas no objeto mais alto. Os resultados são retornados em ordem z. No entanto, o objeto visual que você passa como o parâmetro para o HitTest método determina qual parte da árvore visual que será testada em teste. Você pode fazer teste de clique em toda a árvore visual ou em qualquer parte dela.

Na ilustração a seguir, o objeto circular está por cima dos objetos quadrado e triângulo. Se você estiver interessado apenas em testar o hit do objeto visual cujo valor de ordem z é o mais alto, você pode definir a enumeração de teste de acerto visual para retornar Stop do HitTestResultCallback para parar a travessia de teste de acertos após o primeiro item.

Diagram of the z-order of a visual tree
Diagrama da ordem z- de uma árvore visual

Se você quiser enumerar todos os objetos visuais em um ponto ou geometria específica, retorne Continue do HitTestResultCallback. Isso significa que você pode realizar teste de clique de objetos visuais que estão sob outros objetos, mesmo que eles estejam totalmente encobertos. Consulte o código de exemplo na seção "Usando um retorno de chamada de resultados do teste de clique" para obter mais informações.

Observação

Um objeto visual que é transparente também pode passar por teste de clique.

Usando o teste de clique padrão

Você pode identificar se um ponto está dentro da geometria de um objeto visual, usando o HitTest método para especificar um objeto visual e um valor de coordenada de ponto para testar. O parâmetro de objeto visual identifica o ponto de partida para a pesquisa de teste de clique na árvore visual. Se um objeto visual for encontrado na árvore visual cuja geometria contém a coordenada, ele será definido como a VisualHit propriedade de um HitTestResult objeto. O HitTestResult é então retornado do HitTest método. Se o ponto não estiver contido na subárvore visual que você está testando, HitTest retornará null.

Observação

O teste de clique padrão sempre retorna o objeto mais alto na ordem z. Para identificar todos os objetos visuais, mesmo aqueles que podem estar totalmente ou parcialmente encobertos, use um retorno de chamada de resultado do teste de clique.

O valor de coordenada que você passa como o parâmetro de ponto para o método deve ser relativo ao espaço de coordenadas do objeto visual contra o HitTest qual você está testando. Por exemplo, se você tem objetos visuais aninhados definidos em (100, 100) no espaço de coordenadas do pai, o teste de clique de um objeto visual filho em (0, 0) é equivalente ao teste de clique em (100, 100) no espaço de coordenadas do pai.

O código a seguir mostra como configurar manipuladores de eventos do mouse para um UIElement objeto que é usado para capturar eventos usados para teste de ocorrência.

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

Como a árvore visual afeta testes de clique

O ponto de partida na árvore visual determina quais objetos são retornados durante a enumeração de teste de clique de objetos. Se você tem vários objetos nos quais deseja realizar o teste de clique, o objeto visual usado como o ponto de partida na árvore visual deve ser o ancestral comum de todos os objetos de interesse. Por exemplo, se estiver interessado em realizar testes de clique no elemento botão e no objeto visual de desenho no diagrama a seguir, você terá que definir o ancestral comum de ambos como o ponto de partida na árvore visual. Nesse caso, o elemento tela é o ancestral comum do elemento botão e do objeto visual de desenho.

Diagram of a visual tree hierarchy
Diagrama de uma hierarquia de árvore visual

Observação

A IsHitTestVisible propriedade obtém ou define um valor que declara se um objeto derivado pode ser retornado como um UIElementresultado de teste de acerto de alguma parte de seu conteúdo renderizado. Isso permite que você altere seletivamente a árvore visual para determinar quais objetos visuais estão envolvidos em um teste de clique.

Usando um retorno de chamada de resultado do teste de clique

Você pode enumerar todos os objetos visuais em uma árvore visual cuja geometria contém um valor de coordenada especificado. Isso permite que você identifique todos os objetos visuais, mesmo aqueles que podem estar totalmente ou parcialmente encobertos por outros objetos visuais. Para enumerar objetos visuais em uma árvore visual, use o HitTest método com uma função de retorno de chamada de teste de ocorrência. A função de retorno de chamada de teste de clique é chamada pelo sistema quando o valor de coordenada especificado está contido em um objeto visual.

Durante a enumeração de resultados do teste de clique, você não deve realizar nenhuma operação que modifica a árvore visual. A adição ou remoção de objetos da árvore visual enquanto ela está sendo percorrida poderá resultar em comportamento imprevisível. Você pode modificar com segurança a árvore visual após o retorno do HitTest método. Talvez você queira fornecer uma estrutura de dados, como um ArrayList, para armazenar valores durante a enumeração de resultados de teste de ocorrência.

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

O método do retorno de chamada do teste de clique define as ações que são realizadas quando um teste de clique é identificado em um determinado objeto visual na árvore visual. Depois de executar as ações, você retorna um HitTestResultBehavior valor que determina se deve continuar a enumeração de quaisquer outros objetos visuais ou não.

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

Observação

A ordem de enumeração de objetos visuais de ocorrência é pela ordem z. O objeto visual no nível mais alto da ordem z é o primeiro objeto enumerado. Quaisquer outros objetos visuais enumerados estão em níveis decrescentes da ordem z. Esta ordem de enumeração corresponde à ordem de renderização dos elementos visuais.

Você pode parar a enumeração de objetos visuais a qualquer momento na função de retorno de chamada de teste de ocorrência, retornando Stop.

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

Usando um retorno de chamada de filtro de teste de clique

Você pode usar um filtro de teste de clique opcional para restringir os objetos que são passados aos resultados do teste de clique. Isso permite que você ignore as partes da árvore visual que você não está interessado em processar nos seus resultados do teste de clique. Para implementar um filtro de teste de ocorrência, defina uma função de retorno de chamada de filtro de teste de acertos e passe-a como um valor de parâmetro ao chamar o HitTest método.

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

Se você não quiser fornecer a função opcional de retorno de chamada do filtro de teste de ocorrência, passe um null valor como parâmetro para o HitTest método.

// 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
Aparando uma árvore visual

A função de retorno de chamada de filtro de teste de clique permite que você enumere através de todos os elementos visuais cujo conteúdo renderizado contém as coordenadas especificadas. No entanto, você talvez queira ignorar determinadas ramificações da árvore visual, que você não está interessado em processar em sua função de retorno de chamada de resultados do teste de clique. O valor retornado da função de retorno de chamada do filtro de teste de clique determina o tipo de ação que a enumeração dos objetos visuais deve tomar. Por exemplo, se você retornar o valor, , ContinueSkipSelfAndChildrenpoderá remover o objeto visual atual e seus filhos da enumeração de resultados do teste de ocorrência. Isso significa que a função de retorno de chamada de resultados do teste de clique não verá esses objetos em sua enumeração. Aparar a árvore visual de objetos diminui a quantidade de processamento durante a passagem da enumeração de resultados do teste de clique. No exemplo de código a seguir, o filtro ignora os rótulos e seus descendentes e faz testes de clique de todo o resto.

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

Observação

O retorno de chamada de filtro do teste de clique às vezes será chamado em casos nos quais o retorno de chamada de resultados do teste de clique não é chamado.

Substituindo o teste de clique padrão

Você pode substituir o suporte de teste de acerto padrão de um objeto visual substituindo o HitTestCore método. Isso significa que, quando você invoca o HitTest método, sua implementação substituída de HitTestCore é chamada. O método substituído é chamado quando um teste de clique cair dentro do retângulo delimitador do objeto visual, mesmo que a coordenada ficar fora do conteúdo renderizado do objeto visual.

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

Pode haver ocasiões em que você deseja realizar teste de clique no retângulo delimitador e no conteúdo renderizado de um objeto visual. Usando o valor do parâmetro em seu método substituído HitTestCore como o parâmetro para o PointHitTestParameters método HitTestCorebase, você pode executar ações com base em uma batida do retângulo delimitador de um objeto visual e, em seguida, executar um segundo teste de acerto em relação ao conteúdo renderizado do objeto visual.

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

Confira também