Почему позиция проверки попадания TextBox на Canvas отличается от ее фактической позиции?

У меня есть Canvas с несколькими элементами, такими как Line, Path и Text Box. В событии MouseOver Canvas есть HitTest для всех из них, например:

bool HitTest( Point p )
{
  return VisualTreeHelper.HitTest( textBox, p ) != null;
}

Это работает с точностью до пикселя для линии и пути, но не работает должным образом для текстового поля.

Например, когда TextBox расположен на 50, 50 вот так:

Canvas.SetLeft( textBox, 50.0 );
Canvas.SetTop( textBox, 50.0 );

он отображается в этой позиции правильно: если текст имеет высоту около 20 пикселей и ширину 50 пикселей, ограничивающий прямоугольник на экране примерно равен L=50 T=50 R=100 B=70.

Однако функция HitTest возвращает false в этом прямоугольнике и возвращает true только в прямоугольнике L=0 T=0 R=50 B=20. Другими словами, тест на попадание знает размер текстового поля, но игнорирует его расположение не на 0,0.

Почему это происходит и как обойти это? (У меня такое ощущение, что это как-то связано с тем, что для позиционирования элементов Line и Path я не использую SetLeft/SetTop)


person stijn    schedule 13.12.2012    source источник


Ответы (1)


Просто потому, что TextBox ничего не знает о том, что он расположен на холсте, и, следовательно, ничего о «фактической позиции».

Когда вы вызываете VisualTreeHelper.HitTest(textBox, p), точка p задается в координатах относительно визуального элемента textBox. Не имеет значения, где в Canvas или любом другом возможном контейнере находится TextBox.

То, что вы получаете «правильные» координаты (относительно вашего холста) для линии и пути, просто потому, что вы не устанавливаете для них Canvas.Left или Canvas.Top.

Если вам нужны координаты относительно холста, вы обычно делаете что-то вроде этого:

bool HitTest(UIElement element, Point p)
{
    var elementPoint = new Point(
        p.X - Canvas.GetLeft(element),
        p.Y - Canvas.GetTop(element));

    return VisualTreeHelper.HitTest(element, elementPoint) != null;
}

или альтернативно

bool HitTest(UIElement element, Point p)
{
    var elementPoint = canvas.TransformToDescendant(element).Transform(p);

    return VisualTreeHelper.HitTest(element, elementPoint) != null;
}
person Clemens    schedule 13.12.2012
comment
Таким образом, единственным обходным путем является вычитание левых/верхних координат из точки для проверки попадания? - person stijn; 13.12.2012
comment
хорошо, не знал о TransformToDescendant - person stijn; 13.12.2012
comment
Преимущество TransformToDescentent также в том, что он работает с любым контейнерным элементом управления, а не только с Canvas. - person Clemens; 13.12.2012