Vahid
Vahid

Reputation: 5444

Improve hit testing in the visual layer in WPF

I'm using the code below for Hit Testing in the visual layer. I want to get hits when I click on the lines that are drawn in Drawing visual. But since lines are narrow, I'm not getting good results.

One solution that comes to my mind is increasing the area mouse covers when clicked. This way I'll make sure that the mouse hits the lines even if I click a little bit further from the line.

How can I achieve this? Or what else do you suggest to improve this situation?

var x = MousePos.RightDown.X;
var y = MousePos.RightDown.Y;

var drawing = MyCanvas.GetRebarsVisual();

var pt = new Point(x,y);

var result = VisualTreeHelper.HitTest(drawing, pt);

if (result != null)
{
    MessageBox.Show("You clicked on the line!");
}

Upvotes: 2

Views: 3202

Answers (1)

Adriano Repetti
Adriano Repetti

Reputation: 67070

Using VisualTreeHelper.HitTest() overloaded function where you can specify a HitTestParameters: use a rectangle geometry centered on pt (but bigger than one point) passed with GeometryHitTestParameters:

var hitRect = new Rect(x - 2, y - 2, 4, 4);
VisualTreeHelper.HitTest(drawing, null, null,
    new GeometryHitTestParameters(new RectangleGeometry(hitRect)));

Note that we're using a rectangular geometry in this example but a better approximation (especially for touch screens) is a circle (EllipseGeometry).

Now you know what to call but you need a result, that overload has not a return value instead it uses a callback function where you can accumulate multiple hits (to pick one according to more complex rules). In our example we don't need it so we just stop at first hit:

bool result = false;
var hitRect = new Rect(x - 2, y - 2, 4, 4);
VisualTreeHelper.HitTest(drawing, null,
    htr => { result = true; return  HitTestResultBehavior.Stop; },
    new GeometryHitTestParameters(new RectangleGeometry(hitRect)));

Note that you may even directly execute code:

    htr => {
        MessageBox.Show("You clicked on the line!");
        return  HitTestResultBehavior.Stop;
    },

If you use it often you may write a more generic method for that (for example with an optional parameter for rectangle size).

If you just don't want to know if there is a hit or not (but you also want to know which object) then you can use second callback function (HitTestResultCallback), its parameter (the one called htr in my previous example) is a class derived from HitTestResult and even in base class there is a property named VisualHit which is the visual object (as a generic DependencyObject then you may need casting) you're looking for.

Upvotes: 3

Related Questions