3

I would like to use the WPF HitTest ability on some shapes without showing them onscreen. Is this possible? This code doesn't work the way I expected it to. How do I make this work?

[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));
}
Brannon
  • 5,324
  • 4
  • 35
  • 83

2 Answers2

1

The hit testing is based on the rendering of the element so you should either measure and arrange shape. e.g.

shape.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
shape.Arrange(new Rect(shape.DesiredSize));

Or you could just use the FillContains method of the Geometry. e.g.

var result = shape.Data.FillContains(ToPoint(target));
AndrewS
  • 6,054
  • 24
  • 31
  • This is great info! The Measure/Arrange does get rid of the null result on HitTest. Unfortunately it's still not working in my scenario. Both of these approaches return false positives, and I can't see why. Do I need to convert my point from some absolute coordinate to a local coordinate? – Brannon Jun 13 '13 at 19:37
  • Yes the hit testing would be relative to the element. I would think the HitTest would be based on the actual size of the element based on how it was measured/arranged (i.e. whether it was stretched). I would think the FillContains would be based on the natural size of the geometry. – AndrewS Jun 14 '13 at 03:22
  • I've updated the sample code to use your suggestion but to show that it still doesn't work. – Brannon Jun 17 '13 at 20:55
  • The values you are using are pretty small. I'm guessing it is the tolerance. The overload of FillContains that takes a point passes the StandardFlatteningTolerance to the overload that takes the tolerance and tolerance type and the StandardFlatteningTolerance is 0.25. If you use a smaller tolerance then the method returns false as you were expecting for new Point(). Another option is to increase the values you are using. So if you took your sample and multiplied each by 10 then it also would have worked. – AndrewS Jun 17 '13 at 21:22
  • Yes, the tolerance was killing me! Is there a way to set it on VisualTreeHelper? I don't see anything obvious. – Brannon Jun 17 '13 at 22:09
  • I don't see anything but typically you don't have an element with bounds less than one. If the path will be stretched then arrange it with a larger size than the desired and scale the point. – AndrewS Jun 18 '13 at 01:06
-1

I think your CreateWpfShapeFromList must implemented AddVisualChild methord to your shape's visual children.

Because i reflected VisualTreeHelper and found HitTest use GetVisualChild interanlly, so try my solution.

  • In addition, if your children are not inherited from DrawingVisual, you must override Measure & Arrage. Please attention that. – muzizongheng Jun 17 '13 at 08:51
  • Please look at my newer sample code. I don't see where AddVisualChild has anything to do with it. – Brannon Jun 17 '13 at 20:56