Определите, какой элемент управления ближе всего к указателю мыши - PullRequest
5 голосов
/ 19 октября 2010

В моем приложении на C # (.NET 2) я бы хотел определить, какой элемент управления закрыт для мыши.

Я могу придумать несколько способов сделать это, которые не будут работать правильно.Я мог бы использовать свойство Control.Location, но это просто дает мне верх / лево, и мышь может быть на другой стороне элемента управления.Я мог бы вычислить центральную точку элемента управления, но большие элементы управления могли бы исказить это (находясь рядом с краем элемента управления считается как находящееся близко к элементу управления).

Так что в основном у меня есть группа прямоугольников на холстеи точка.Мне нужно найти прямоугольник, ближайший к точке.

(В идеале я бы тоже хотел знать расстояние между точкой и прямоугольником).

Есть идеи?

Ответы [ 5 ]

3 голосов
/ 19 октября 2010

Вам нужно найти следующее:- Расстояние до ближайшего угла- Расстояние до ближайшего края- (опционально) расстояние до центра

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

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

public class HitControl {

    public Control ThisControl;

    private Rectangle ControlBounds;
    private Point Center;

    public HitControl (Control FormControl) {
        ControlBounds = FormControl.Bounds;
        Center = new Point(ControlBounds.X + (ControlBounds.Width/2), ControlBounds.Y + (ControlBounds.Height/2));
    }

    //  Calculate the minimum distance from the left, right, and center
    public double DistanceFrom(Point TestPoint) {

        //  Note:  You don't need to consider control center points unless
        //  you plan to allow for controls placed over other controls... 
        //  Then you need to test the distance to the centers, as well, 
        //  and pick the shortest distance of to-edge, to-side, to-corner

        bool withinWidth = TestPoint.X > ControlBounds.X && TestPoint.X < ControlBounds.X + ControlBounds.Width;
        bool withinHeight = TestPoint.Y > ControlBounds.Y && TestPoint.Y < ControlBounds.Y + ControlBounds.Height;

        int EdgeLeftXDistance = Math.Abs(ControlBounds.X - TestPoint.X);
        int EdgeRightXDistance = Math.Abs(ControlBounds.X + ControlBounds.Width - TestPoint.X);

        int EdgeTopYDistance = Math.Abs(ControlBounds.Y - TestPoint.Y);
        int EdgeBottomYDistance = Math.Abs(ControlBounds.Y + ControlBounds.Height - TestPoint.Y);

        int EdgeXDistance = Math.Min(EdgeLeftXDistance, EdgeRightXDistance);
        int EdgeYDistance = Math.Min(EdgeTopYDistance, EdgeBottomYDistance);


        // Some points to consider for rectangle (100, 100, 100, 100):
        //  - (140, 90):  Distance to top edge
        //  - (105, 10):  Distance to top edge
        //  - (50, 50):   Distance to upper left corner
        //  - (250, 50):  Distance to upper right corner
        //  - (10, 105):  Distance to left edge
        //  - (140, 105):  Distance to top edge
        //  - (105, 140):  Distance to left edge
        //  - (290, 105):  Distance to right edge
        //  - (205, 150):  Distance to right edge
        //  ... and so forth


        //  You're within the control
        if (withinWidth && withinHeight) {
            return Math.Min(EdgeXDistance, EdgeYDistance);
        }

        //  You're above or below the control
        if (withinWidth) {
            return EdgeYDistance;
        }

        //  You're to the left or right of the control
        if (withinHeight) {
            return EdgeXDistance;
        }

        //  You're in one of the four outside corners around the control.
        //  Find the distance to the closest corner
        return Math.Sqrt(EdgeXDistance ^ 2 + EdgeYDistance ^ 2);


    }

    public bool ContainsPoint (Point TestPoint) {
        return ControlBounds.Contains(TestPoint);
    }


}



//  Initialize and use this collection
List<HitControl> hitControls = (from Control control in Controls
                                select new HitControl(control)).ToList();

Point testPoint = new Point(175, 619);
double distance;
double shortestDistance = 0;
HitControl closestControl = null;

foreach (HitControl hitControl in hitControls) {

    //  Optional... works so long as you don't have overlapping controls
    //  If you do, comment this block out
    if (hitControl.ContainsPoint(testPoint)) {
        closestControl = hitControl;
        break;
    }

    distance = hitControl.DistanceFrom(testPoint);
    if (shortestDistance == 0 || distance < shortestDistance) {
        shortestDistance = distance;
        closestControl = hitControl;
    }
}

if (closestControl != null) {
    Control foundControl = closestControl.ThisControl;
}
1 голос
/ 19 октября 2010

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

0 голосов
/ 19 октября 2010

Я согласен с Даниэлем в том, что нам нужно: double DistanceFrom (Rect r, Point p);

Но перед этим нам нужно: double DistanceFrom (Line r, Point p);и двойные AngleBetweenPoints (точка p1, точка p2);

0 голосов
/ 19 октября 2010

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

double DistanceFrom(Rect r, Point p);

Затем, для самой простой попытки, выполните итерацию всех элементов управления, рассчитайте расстояние, запомните минимальное расстояние и элемент управления, который его предоставил.

Для расстояния прямоугольника,отметьте this out.

EDIT:

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

0 голосов
/ 19 октября 2010

Вы должны думать с точки зрения прямоугольников:)

  1. Тест: находится ли мышь под контролем?
  2. Если нет: как далеко от какого-либо одного края?

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

...