Улучшение проверки попаданий на визуальном уровне в WPF

Я использую приведенный ниже код для Hit Testing в визуальном слое. Я хочу получать хиты, когда я нажимаю на линии, нарисованные в Drawing визуальном элементе. Но так как линии узкие, я не получаю хороших результатов.

Одно из решений, которое приходит мне на ум, — это увеличение площади покрытия мыши при нажатии. Таким образом я удостоверюсь, что мышь попадет в линии, даже если я щелкну немного дальше от линии.

Как я могу этого добиться? Или что еще вы предлагаете, чтобы улучшить эту ситуацию?

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!");
}

person Vahid    schedule 09.09.2014    source источник
comment
Используя VisualTreeHelper.HitTest, где вы можете указать HitTestParameters, используйте геометрию прямоугольника с центром в pt, но больше, чем одна точка, переданная с GeometryHitTestParameters: VisualTreeHelper.HitTest(drawing, null, null, new GeometryHitTestParameters(...)).   -  person Adriano Repetti    schedule 10.09.2014
comment
@AdrianoRepetti Спасибо, я просто изучал этот вариант. Можете ли вы помочь мне со статьей или примером?   -  person Vahid    schedule 10.09.2014
comment
Добавлено в комментарий, замените ... на new RectangleGeometry(new Rect(x - 2, y - 2, 4, 4)). Ну... для примера. Прямоугольник является самым простым, но, особенно если вы планируете использовать свое приложение с сенсорным экраном, круг лучше представляет большую область проверки попадания.   -  person Adriano Repetti    schedule 10.09.2014
comment
@AdrianoRepetti Большое спасибо, Адриано, я добираюсь туда, но моя проблема в том, где я должен назначить VisualTreeHelper..., как вы можете видеть в моем примере, я назначаю его результату. Я совершенно новичок в этом.   -  person Vahid    schedule 10.09.2014


Ответы (1)


Используя перегруженную функцию VisualTreeHelper.HitTest(), где вы можете указать HitTestParameters: используйте геометрию прямоугольника с центром в pt (но больше одной точки), переданную с GeometryHitTestParameters:

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

Обратите внимание, что в этом примере мы используем прямоугольную геометрию, но лучшим приближением (особенно для сенсорных экранов) является круг (EllipseGeometry).

Теперь вы знаете, что вызывать, но вам нужен результат, эта перегрузка не имеет возвращаемого значения, вместо этого она использует функцию обратного вызова, где вы можете накапливать несколько попаданий (выбирать одно по более сложным правилам). В нашем примере он нам не нужен, поэтому мы просто останавливаемся при первом попадании:

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)));

Обратите внимание, что вы даже можете напрямую выполнить код:

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

Если вы используете его часто, вы можете написать для этого более общий метод (например, с необязательным параметром для размера прямоугольника).

Если вы просто не хотите знать, есть ли попадание или нет (но вы также хотите знать, какой объект), вы можете использовать вторую функцию обратного вызова (HitTestResultCallback), ее параметр (тот, что называется htr в моем предыдущем примере) класс, производный от HitTestResult< /a> и даже в базовом классе есть свойство с именем VisualHit, который является визуальным объектом (как общий DependencyObject, тогда вам может понадобиться приведение), который вы ищете.

person Adriano Repetti    schedule 09.09.2014
comment
Код работает отлично, большое спасибо. Думаю, только found = true нужно заменить на result = true. Я очень ценю вашу помощь, я много искал в MSDN, но не смог найти полный рабочий пример. Можете ли вы указать мне несколько примеров? - person Vahid; 10.09.2014
comment
Вы правы, исправлено! Примеры IMO MSDN об этих методах довольно хороши (даже если они короткие). Для начала взгляните на это и это. - person Adriano Repetti; 10.09.2014
comment
Спасибо Адриано, эти статьи помогли мне лучше понять ответ. - person Vahid; 10.09.2014
comment
Вы мне уже очень помогли. Еще один вопрос, если нужно, скажите, пожалуйста, я опубликую его как отдельный вопрос. До того, как я склонился к HitTest, я делал это по-своему. Я имею в виду, что я искал в определениях строк вручную и искал конкретную строку, на которой был мой указатель. Таким образом, я мог бы, например, изменить его цвет и тому подобное. Теперь, когда я использую HitTesting, как мне получить точную строку, на которую я указываю. Как я могу получить ссылку или что-то подобное, чтобы я мог внести в нее изменения. - person Vahid; 10.09.2014
comment
Да, вы можете сделать это в функции обратного вызова. Параметр, который я назвал htr в лямбда-выражении (хорошо, у меня не слишком много фантазии) — это HitTestResult (один из его производных классов). Даже в базовом абстрактном классе есть свойство VisualHit, который соответствует DependencyObject. - person Adriano Repetti; 10.09.2014
comment
О, я думаю, у меня будут проблемы здесь. Поэтому, чтобы получить точную линию, по которой щелкнули, мне нужно будет нарисовать каждую линию на отдельных визуальных элементах рисования. я прав? В моей программе я нарисовал все линии в одном визуальном изображении, так что VisualHit возвращает этот единственный визуальный элемент со всеми линиями на нем? - person Vahid; 10.09.2014
comment
Насколько я знаю да, когда у вас есть визуальный элемент, вы должны выполнить для него собственное тестирование попаданий. Вы можете опубликовать это как еще один вопрос, может быть, у кого-то есть хорошее/быстрое/простое решение для этого (нет необходимости перебирать строки вручную). - person Adriano Repetti; 10.09.2014
comment
Давайте продолжим обсуждение в чате. - person Vahid; 10.09.2014
comment
Спасибо, я спросил это здесь. stackoverflow.com/questions/25754861/ - person Vahid; 10.09.2014