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

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

  1. Мне нужно убедиться, что курсор находится внутри гексагональной формы (между краями каждого гекса есть небольшой зазор). Поэтому, если я щелкну один из углов элемента управления, этот элемент управления не будет реагировать на этот щелчок, поскольку он находится за пределами шестиугольника.
  2. Когда я нажимаю, мне нужно определить, какой элемент управления владеет шестигранником, на который я нажимаю.

person Thomas O'Connor    schedule 16.01.2013    source источник
comment
Чтобы уточнить, мы говорим о Winforms или WPF?   -  person KingCronus    schedule 16.01.2013
comment
Это очень простая проблема юзабилити пользовательского интерфейса. Он ведет себя так, как должен, самый верхний должен получить щелчок. Если вы хотите, чтобы пользователь мог выбрать другой, вам нужен другой пользовательский интерфейс. Может быть, поле со списком или кнопки со стрелками, чтобы выбрать один.   -  person Hans Passant    schedule 16.01.2013
comment
@HansPassant: это проблема удобства использования, если она не работает. Концепция и требование совершенно корректного пользовательского интерфейса.   -  person musefan    schedule 16.01.2013
comment
Winforms, в основном, цель состоит в том, чтобы пользователь щелкнул шестигранник, чтобы сделать его определенным цветом. Кроме того, карта может быть разных размеров в зависимости от предпочтений пользователя (5x5, 10x10 и т. д.), поэтому каждый гекс находится под собственным контролем.   -  person Thomas O'Connor    schedule 16.01.2013


Ответы (1)


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

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

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

Для этого вам придется хорошенько подумать над математикой!


РЕШЕНИЕ:

Это работает, и я проверил это. Что у меня есть: UserControl, который рисует форму, это называется «ClickControl». Все мои ClickControls находятся внутри Panel под названием mainPanel. Для каждого ClickControl зарегистрировано одно и то же событие MouseClick, в данном случае событие control_MouseClick. Имея все это в виду, вот пример кода:

void control_MouseClick(object sender, MouseEventArgs e)
{
    //get mouse point relative to panel
    var mousePoint = panelMain.PointToClient(Cursor.Position);
    int startX = mousePoint.X;
    int startY = mousePoint.Y;

    //store the best match as we find them
    ClickControl selected = null;
    double? closestDistance = null;

    //loop all controls to find the best match
    foreach (Control c in panelMain.Controls)
    {
        ClickControl control = c as ClickControl;
        if (control != null)
        {
            //calculate the center point of the control relative to the parent panel
            int endX = control.Location.X + (control.Width / 2);
            int endY = control.Location.Y + (control.Height / 2);

            //calculate the distance between the center point and the mouse point
            double distance = Math.Sqrt(Math.Pow(endX - startX, 2) + Math.Pow(endY - startY, 2));

            //if this one is closer then we store this as our best match and look for the next best match
            if (closestDistance == null || closestDistance > distance)
            {
                selected = control;
                closestDistance = distance;
            }
        }
    }

    //`selected` is now the correct control
}

Я уверен, что есть много оптимизаций, которые можно сделать, если у вас есть проблемы с производительностью, но это, по крайней мере, рабочее начало!

person musefan    schedule 16.01.2013
comment
Это может быть именно то, что мне нужно! Большое спасибо. - person Thomas O'Connor; 16.01.2013