5 Stimmen

Ermitteln, welches Steuerelement dem Mauszeiger am nächsten ist

In meinem C# (.NET 2) app möchte ich bestimmen, welches Steuerelement an der Maus geschlossen ist.

Ich kann mir ein paar Möglichkeiten vorstellen, die nicht ganz funktionieren. Ich könnte die Control.Location Eigenschaft, aber das gibt mir nur oben/links, und die Maus könnte auf der anderen Seite des Steuerelements sein. Ich könnte den Mittelpunkt eines Steuerelements berechnen, aber große Steuerelemente würden dies verzerren (in der Nähe des Randes eines Steuerelements zählt als nahe am Steuerelement).

Also im Grunde habe ich ein Bündel von Rechtecken auf einer Leinwand und einem Punkt. Ich muss das Rechteck finden, das dem Punkt am nächsten liegt.

(Idealerweise würde ich auch gerne den Abstand zwischen Punkt und Rechteck kennen).

Irgendwelche Ideen?

0 Stimmen

Hat Ihnen eine dieser Antworten geholfen?

3voto

James King Punkte 6082

Sie müssen Folgendes finden:
- Entfernung zur nächstgelegenen Ecke
- Abstand zur nächsten Kante
- (optional) Abstand zum Zentrum

Im Grunde genommen wollen Sie den kleineren dieser drei Werte. Nehmen Sie den Mindestwert für zwei Kontrollen, um festzustellen, welcher Wert näher liegt.

Beginnen Sie beim Laden des Formulars mit der Iteration aller Steuerelemente auf dem Formular und erstellen Sie eine Sammlung der folgenden Klasse.

Um das einem Punkt am nächsten liegende Steuerelement zu finden, muss die Sammlung durchlaufen werden (siehe Code unten). Behalten Sie das Steuerelement mit dem geringsten Abstand im Auge, das Sie bisher gefunden haben. Sie können auf ContainsPoint() testen, wenn Sie wollen... wenn Sie ein Steuerelement finden, bei dem der Punkt innerhalb der Grenzen des Steuerelements liegt, haben Sie Ihr Steuerelement (solange Sie keine überlappenden Steuerelemente haben). Andernfalls ist das Steuerelement mit dem kürzesten Abstand zum Mittelpunkt/Rand Ihr Steuerelement, wenn Sie das Ende der Sammlung erreichen.

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

1voto

Saeed Amiri Punkte 21834

Prüfen Sie zunächst, ob sich der Punkt in einem beliebigen Rechteck . Falls nicht, können Sie den Abstand zwischen Ihrem Punkt und jedem Liniensegment mit dem Algorithmus in diese . Sie können auch die 4 Segmente Ihres Steuerelements finden, so dass Sie eine Liste (zum ersten Mal eingeleitet) von vier Segmenten (Bestimmung der Steuerelementseiten) haben und nun das nächstgelegene Segment, sein nächstgelegenes Rechteck, finden können.

0voto

Onkelborg Punkte 3887

Man muss in Rechtecken denken :)

  1. Test: Ist die Maus unter Kontrolle?
  2. Wenn nicht: Wie weit ist der Abstand zu einer einzelnen Kante?

Dann müssen Sie wissen, an welchen Steuerelementen Sie interessiert sind, das Formular ist zum Beispiel ein Steuerelement.

0voto

Daniel Mošmondor Punkte 19346

Erstellen Sie zunächst eine Methode, die den Abstand zwischen dem Rechteckrand und einem beliebigen Punkt berechnet. Die Signatur für diese Methode sollte sein:

double DistanceFrom(Rect r, Point p);

Dann, für den einfachsten Versuch, iteriere alle Kontrollen, berechne den Abstand, erinnere dich an den minimalen Abstand und die Kontrolle, die ihn geliefert hat.

Für den Rechteckabstand, prüfen Sie diese aus.

EDITAR:

Tatsächlich können Sie eine sortierte Liste von Steuerelementen beibehalten, so dass Sie immer die erste, die näher an der Spitze ist, und halten Sie diese Liste, wie die Maus bewegt - es kann sich als effizienter in Bezug auf die Geschwindigkeit sein. Interessantes Thema aber :)

0voto

QYY Punkte 182

Ich stimme mit Daniel überein, dass wir das brauchen: double DistanceFrom(Rect r, Point p);

Aber vorher müssen wir: double DistanceFrom(Linie r, Punkt p); und double AngleBetweenPoints(Punkt p1, Punkt p2);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X