如何使VisualTreeHelper.HitTest在单元测试中生效?

3

我想在不显示形状的情况下使用WPF HitTest功能。这可能吗? 这段代码没有按照我的期望工作。我该如何使它起作用?

[Test, Ignore, RequiresSTA]
public void VerifyFillContains()
{
    // make a simple triangle:
    var seg1 = new System.Windows.Media.LineSegment(new Point(0.2, 0.1), false);
    var seg2 = new System.Windows.Media.LineSegment(new Point(0.3, 0.8), false);
    var figure = new PathFigure(new Point(0.7, 0.5), new List<PathSegment>{seg1, seg2}, true);

    var sg = new PathGeometry();
    sg.Figures.Add(figure);

    if (sg.CanFreeze)
        sg.Freeze();

    // these don't work:
    Assert.IsFalse(sg.FillContains(new Point()));
    Assert.IsFalse(sg.FillContains(new Point(1.0, 1.0)));
    Assert.IsTrue(sg.FillContains(new Point(0.5, 0.5)));

    var path = new Path { Data = sg, Fill = Brushes.Black };
    path.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
    path.Arrange(new Rect(path.DesiredSize));

    // and these don't work:
    var result = VisualTreeHelper.HitTest(path, new Point(0.0, 0.0));
    Assert.IsFalse(result != null && Equals(result.VisualHit, path));
    result = VisualTreeHelper.HitTest(path, new Point(1.0, 1.0));
    Assert.IsFalse(result != null && Equals(result.VisualHit, path));
    result = VisualTreeHelper.HitTest(path, new Point(0.5, 0.5));
    Assert.IsTrue(result != null && Equals(result.VisualHit, path));
}
2个回答

1
命中测试基于元素的渲染,因此您应该测量和排列形状。例如:shape
shape.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
shape.Arrange(new Rect(shape.DesiredSize));

或者你可以直接使用 Geometry 的 FillContains 方法,例如:

var result = shape.Data.FillContains(ToPoint(target));

这是很棒的信息!Measure/Arrange确实可以消除HitTest上的空结果。不幸的是,在我的情况下它仍然无法工作。这两种方法都会返回错误的结果,我不知道为什么。我需要将我的点从某个绝对坐标转换为本地坐标吗? - Brannon
是的,命中测试将与元素相关。我认为HitTest将基于元素的实际大小,根据它是如何被测量/排列的(即是否被拉伸)。我认为FillContains将基于几何图形的自然大小。 - AndrewS
我已经更新了示例代码以使用您的建议,但仍然无法工作。 - Brannon
你使用的值相当小。我猜这是公差的原因。FillContains过载函数,将一组点传递给了另一个接受公差和公差类型的函数,而StandardFlatteningTolerance为0.25。如果您使用更小的公差,则该方法就会像您期望的那样返回false。另一个选择是增加您使用的值。因此,如果您将示例中的每个值乘以10,它也可以正常工作。 - AndrewS
是的,这个公差真的让我很烦恼!在VisualTreeHelper上有设置它的方法吗?我没有看到任何明显的东西。 - Brannon
我什么也没看到,但通常情况下,您不会有一个边界小于一的元素。如果路径将被拉伸,则请使用比所需更大的大小进行排列并缩放点。 - AndrewS

-1

我认为你的CreateWpfShapeFromList方法必须实现AddVisualChild方法来添加你的形状到可视化子元素中。

因为我反射了VisualTreeHelper并发现HitTest在内部使用GetVisualChild,所以请尝试我的解决方案。


此外,如果您的子类不是从DrawingVisual继承而来,则必须重写Measure和Arrage方法。请注意。 - muzizongheng
请查看我的新示例代码。我不明白AddVisualChild与此有任何关系。 - Brannon

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