I'm trying to hit-test a bunch of UserControls on a Canvas. I don't want the HitTest() to walk the whole way through the visual tree, so I'm using the FilterCallback to make sure I only hit-test the UserControl.
My problem is that the UserControl never hits, it should, but it doesn't. If I use the FilterCallback, I return that it hit nothing. If I let the HitTest run through the visual tree, it skips the UserControl.
Here's some code:
<Canvas x:Name="Container">
<UserControl>
<Grid>
<Rectangle />
</Grid>
</UserControl>
<UserControl>
<Grid>
<Rectangle />
</Grid>
</UserControl>
</Canvas>
...
VisualTreeHelper.HitTest(Container, OnFilter, OnResult, myPoint);
...
private void OnResult(DependencyObject o)
{
//I'll get the Rectangle here, but never the userControl
}
private void OnFilter(DependencyObject o)
{
//I will get the UserControl here, but even when I do nothing more than continue, it will not trigger a visualHit. But the child rectangle will.
}
I had this same problem of HitTest not finding a user control. Apparently this is by design (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/005dad03-c8eb-405f-9567-50653a0e612c).
I worked around this by handling the hit of some element inside the user control, and then finding the parent user control using the VisualTreeHelper.GetParent method. I'm not very familiar with WPF yet, so I'm not sure if it would be better to use FrameworkElement.Parent property.
However, here is my method for finding the user control (or any visual parent of some required type) after first finding some of its content elements by hit test:
public static T GetVisualParent<T>(this DependencyObject element) where T : DependencyObject
{
while (element != null && !(element is T))
element = VisualTreeHelper.GetParent(element);
return (T)element;
}
I know it's pretty damn late to answer this but here goes: a different approach is to override HitTestCore on the UserControl and provide it with the default behavior that would be expected from it:
protected override System.Windows.Media.HitTestResult HitTestCore(System.Windows.Media.PointHitTestParameters hitTestParameters)
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
(of course you could complicate things and hit-test the actual children or the combination of their bounding boxes, but for me the bounding box of the user control was good enough; also, if you want to hit-test against geometry you need to override that second overload as well.)
This makes it work as expected, filtering out the children when using HitTestFilterBehavior.ContinueSkipChildren
in the filter.