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 clique permite que você determine se um valor de geometria ou de ponto está dentro do conteúdo renderizado de um Visual, permitindo que você implemente um comportamento de interface do usuário, como um retângulo de seleção para selecionar vários objetos.
Cenários de teste de clique
A classe UIElement fornece o método InputHitTest, que permite realizar um teste de colisão em relação a um elemento a partir de um valor de coordenada fornecido. Em muitos casos, o método InputHitTest fornece a funcionalidade desejada para implementar o teste de colisão de elementos. No entanto, há vários cenários em que talvez seja necessário implementar testes de clique na camada visual.
Teste de clique em objetos nãoUIElement: isso se aplica se você estiver testando objetos não UIElement, como DrawingVisual ou objetos gráficos.
Teste de colisão usando uma geometria: isso se aplica se você precisar realizar o teste de colisão usando um objeto de geometria em vez do valor da coordenada de um ponto.
Teste de colisão em múltiplos objetos: isso se aplica quando você precisa testar colisões 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 clique UIElement: isso se aplica quando você precisa ignorar a política de teste de clique UIElement, que leva em consideração fatores como quando um elemento está desabilitado ou invisível.
Nota
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
A finalidade dos métodos HitTest na classe VisualTreeHelper é determinar se um valor de coordenada de geometria ou ponto está dentro do conteúdo renderizado de um determinado objeto, como um controle ou elemento gráfico. Por exemplo, você pode usar o teste de clique para determinar se um clique do mouse dentro do retângulo delimitador de um objeto está 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 de um objeto não retangular e seu retângulo delimitador.
diagrama
Diagrama da região de teste de colisão válido
Teste de clique e ordem z
A camada visual da Windows Presentation Foundation (WPF) dá suporte a testes de colisão 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 método HitTest determina qual parte da árvore visual que será atingida no teste. Você pode fazer teste de clique em toda a árvore visual ou em qualquer parte dela.
Na ilustração a seguir, o objeto círculo está sobre os objetos quadrado e triângulo. Se você estiver interessado apenas em testar o objeto visual cujo valor de ordem z é o mais alto, você poderá definir a enumeração de teste de clique visual para retornar Stop do HitTestResultCallback para interromper a passagem de teste de clique após o primeiro item.
Diagrama da ordem z de uma árvore visual
Se você quiser enumerar todos os objetos visuais em um ponto ou geometria específico, 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.
Nota
Um objeto visual que é transparente também pode ser submetido a um teste de colisão.
Usando o teste de clique padrão
Você pode identificar se um ponto está dentro da geometria de um objeto visual usando o método HitTest 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 propriedade VisualHit de um objeto HitTestResult. O HitTestResult é retornado do método HitTest. Se o ponto não estiver contido na subárvore visual que você estiver testando, HitTest retornará null
.
Nota
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 da coordenada que você passa como o parâmetro de ponto para o método HitTest deve ser relativo ao espaço de coordenadas do objeto visual contra o qual você está fazendo o teste de colisão. 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 objeto UIElement que é usado para capturar eventos usados para teste de colisão.
// 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ê tiver vários objetos que deseja testar, o objeto visual usado como ponto de partida na árvore visual deverá 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.
Diagrama
Diagrama de uma hierarquia de árvore visual
Nota
A propriedade IsHitTestVisible obtém ou define um valor que declara se um objeto derivado de UIElementpode ser retornado como um resultado de teste de clique 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 colisão.
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 identificar todos os objetos visuais, mesmo aqueles que podem ser parcial ou totalmente obscurecidos por outros objetos visuais. Para enumerar objetos visuais em uma árvore visual, use o método HitTest com uma função de retorno de chamada de teste de clique. 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. Adicionar ou remover um objeto da árvore visual enquanto ele está sendo percorrido pode resultar em um comportamento imprevisível. Você pode modificar a árvore visual com segurança após o retorno do método HitTest. Talvez você queira fornecer uma estrutura de dados, como uma ArrayList, para armazenar valores durante a execução da enumeração dos resultados do teste de colisão.
// 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 valor HitTestResultBehavior que determina se deseja continuar a enumeração de 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
Nota
A ordem de enumeração de objetos visuais de ocorrência é pela ordem z. O objeto visual no nível superior da ordem z é o primeiro objeto enumerado. Todos os outros objetos visuais enumerados estão em nível de ordem z decrescente. Essa ordem de enumeração corresponde à ordem de renderização dos visuais.
Você pode interromper a enumeração de objetos visuais a qualquer momento na função de retorno de chamada do teste de clique, 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 clique, você define uma função de retorno de chamada de filtro de teste de clique e passa essa função como um valor de parâmetro ao chamar o método 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
Se não desejar fornecer a função opcional de retorno de chamada de filtro do teste de clique, passe um valor null
como seu parâmetro para o método 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.
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 ContinueSkipSelfAndChildren, poderá remover o objeto visual atual e seus filhos da enumeração de resultados do teste de clique. 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
Nota
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 ao teste de clique padrão de um objeto visual, substituindo o método HitTestCore. Isso significa que, quando você invoca o método HitTest, 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 de parâmetro PointHitTestParameters
no seu método substituído HitTestCore como o parâmetro para o método base HitTestCore, você pode realizar ações com base em uma ocorrência do retângulo delimitador de um objeto visual e, em seguida, realizar um segundo teste de clique no 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
Consulte também
.NET Desktop feedback