在WPF中使用VisualTreeHelper.HitTest出现问题

4
我正在尝试在画布(Canvas)上进行一堆UserControl的命中测试(HitTest())。我不希望HitTest()遍历整个可视树,因此我使用了FilterCallback来确保只对UserControl进行命中测试。
我的问题是UserControl从未被命中,它应该被命中,但实际上并没有。如果我使用FilterCallback,则返回未命中任何内容。如果我让HitTest运行整个可视树,则跳过了UserControl。
下面是一些代码:
<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.
}
2个回答

12

我知道回答有点晚了,但是这里有一个不同的方法:在UserControl上覆盖HitTestCore并为其提供默认行为:

protected override System.Windows.Media.HitTestResult HitTestCore(System.Windows.Media.PointHitTestParameters hitTestParameters)
{
    return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
当然,你可以复杂化事情,并测试实际子元素或其边界框的组合,但对我来说,用户控件的边界框已经足够好了;此外,如果您想针对几何图形进行命中测试,您也需要覆盖第二个重载。
这样做可以按预期工作,在过滤器中使用HitTestFilterBehavior.ContinueSkipChildren时过滤掉子元素。

可以确认这个有效。应该被接受为最佳答案,因为它比手动搜索可视树更好。 - Lennart

2
我曾经遇到过HitTest无法找到用户控件的问题。显然,这是设计上的问题(参考此链接)。
我通过处理用户控件中某个元素的点击,然后使用VisualTreeHelper.GetParent方法找到其父用户控件来解决这个问题。由于我对WPF还不是很熟悉,所以我不确定是否应该使用FrameworkElement.Parent属性。
无论如何,以下是我的方法,可以在首先通过命中测试找到某些内容元素后,找到用户控件(或任何所需类型的可视化父级):
public static T GetVisualParent<T>(this DependencyObject element) where T : DependencyObject
{
    while (element != null && !(element is T))
        element = VisualTreeHelper.GetParent(element);

    return (T)element;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接