C # Ermittelt die Position eines Steuerelements in einem Formular


71

Gibt es eine Möglichkeit, die Position eines Steuerelements in einem Formular abzurufen, wenn sich das Steuerelement möglicherweise in anderen Steuerelementen (z. B. Bedienfeldern) befindet?

Die Eigenschaften Left und Top des Steuerelements geben mir nur die Position innerhalb des übergeordneten Steuerelements an. Was ist jedoch, wenn sich mein Steuerelement in fünf verschachtelten Bedienfeldern befindet und ich seine Position im Formular benötige?

Kurzes Beispiel:

Die Schaltfläche btnA befindet sich an den Koordinaten (10,10) innerhalb des Bedienfelds pnlB.
Das Panel pnlB befindet sich auf den Koordinaten (15, 15) innerhalb des Formulars frmC.

Ich möchte den Standort von btnA auf frmC (25,25).

Kann ich diesen Ort bekommen?

Antworten:


97

Ich kombiniere normalerweise PointToScreenund PointToClient:

Point locationOnForm = control.FindForm().PointToClient(
    control.Parent.PointToScreen(control.Location));

9
Dies ist der REAL ABSOULUTE POSITION stackoverflow.com/questions/4998076/…
PUG

4
Wie unterscheidet sich das von control.PointToScreen(Point.Empty);?
Strongriley

1
@strongriley Keine Ahnung (habe es noch nie ausprobiert und derzeit ist keine Entwicklungsumgebung verfügbar), aber ich kann das in der Dokumentation erwähnte Verhalten nicht erkennen, und wenn ja, gibt es keine Garantie dafür, dass es in zukünftigen Framework-Versionen funktioniert.
Fredrik Mörk

5
@strongriley control.PointToScreen(Point.Empty)gibt den Ort in Bezug auf den Bildschirm an, während die Antwort den Ort mit Bezug auf das Formular der obersten Ebene angibt.
Nawfal

Hey, du hast mir dabei sehr geholfen :) Es zentriert gestaltete Popups in der Mitte der Programmanzeige anstatt in der Mitte aller Bildschirme!
Kokbira

11

Mit der Steuerungsmethode können PointToScreenSie die absolute Position in Bezug auf den Bildschirm ermitteln.

Sie können die Forms- PointToScreenMethode ausführen und mit einfachen Berechnungen die Position des Steuerelements ermitteln.


Das wird nicht genau richtig funktionieren, es sei denn, Sie berücksichtigen die Höhe der Titelleiste und die Breite des Formularrahmens.
MusiGenesis

Wie hilft dies, die absolute Position der Steuerung zu ermitteln? Sie müssen ein Point-Objekt an PointToScreen () übergeben, was in diesem Fall keinen Sinn ergibt.
Tim

@Tim, Sie müssen System.Drawing.Point.Empty in diesem Fall übergeben: var absolutePosition = control.PointToScreen (System.Drawing.Point.Empty);
Umar T.

10

Normalerweise mache ich das so. Funktioniert jedes Mal.

var loc = ctrl.PointToScreen(Point.Empty);

6

Sie können durch die Eltern gehen und ihre Position innerhalb ihrer Eltern notieren, bis Sie zum Formular gelangen.

Edit: So etwas wie (ungetestet):

public Point GetPositionInForm(Control ctrl)
{
   Point p = ctrl.Location;
   Control parent = ctrl.Parent;
   while (! (parent is Form))
   {
      p.Offset(parent.Location.X, parent.Location.Y);
      parent = parent.Parent;
   }
   return p;
}

Ja, ich dachte darüber nach, aber es schien ein unpraktischer Weg zu sein, also hoffte ich, dass es einen anderen Weg gab. Wenn niemand einen anderen Vorschlag hat, werde ich das tun.
Erlend D.

Dies ist der Weg, dies mit einer einfachen rekursiven Funktion zu tun.
MusiGenesis

5

Supergeek, Ihre nicht rekursive Funktion hat nicht das richtige Ergebnis erzielt, meine jedoch. Ich glaube, deine macht eine zu viele Ergänzungen.

private Point LocationOnClient(Control c)
{
   Point retval = new Point(0, 0);
   for (; c.Parent != null; c = c.Parent)
   { retval.Offset(c.Location); }
   return retval;
}

3

Seltsamerweise waren PointToClient und PointToScreen nicht ideal für meine Situation. Vor allem, weil ich mit Offscreen-Steuerelementen arbeite, die keinem Formular zugeordnet sind. Ich fand Sahins Lösung am hilfreichsten, nahm aber die Rekursion heraus und beseitigte die Formularbeendigung. Die folgende Lösung funktioniert für alle meine Steuerelemente, die sichtbar oder nicht sichtbar, formularhaltig oder nicht, IContainered oder nicht. Danke Sahim.

private static Point FindLocation(Control ctrl)
{
    Point p;
    for (p = ctrl.Location; ctrl.Parent != null; ctrl = ctrl.Parent)
        p.Offset(ctrl.Parent.Location);
    return p;
}

1

In meinen Tests gaben sowohl die Lösungen von Hans Kesting als auch von Fredrik Mörk die gleiche Antwort. Aber:

Ich fand eine interessante Diskrepanz in der Antwort mit den Methoden von Raj More und Hans Kesting und dachte, ich würde teilen. Vielen Dank an beide für ihre Hilfe; Ich kann nicht glauben, dass eine solche Methode nicht in das Framework integriert ist.

Bitte beachten Sie, dass Raj keinen Code geschrieben hat und daher meine Implementierung anders sein könnte als er meinte.

Der Unterschied, den ich fand, war, dass die Methode von Raj More oft zwei Pixel größer war (sowohl in X als auch in Y) als die Methode von Hans Kesting. Ich habe noch nicht festgestellt, warum dies geschieht. Ich bin mir ziemlich sicher, dass es etwas damit zu tun hat, dass der Inhalt einen Zwei-Pixel-Rand zu haben scheint eines Windows-Formulars (wie in den äußersten Rändern des Formulars). Bei meinen Tests, die sicherlich nicht erschöpfend waren, bin ich nur auf verschachtelte Steuerelemente gestoßen. Nicht alle verschachtelten Steuerelemente weisen dies jedoch auf. Zum Beispiel habe ich eine TextBox in einer GroupBox, die die Diskrepanz aufweist, eine Schaltfläche in derselben GroupBox jedoch nicht. Ich kann nicht erklären warum.

Beachten Sie, dass bei äquivalenten Antworten der Punkt (0, 0) innerhalb des oben erwähnten Inhaltsrahmens liegt. Daher glaube ich, dass ich die Lösungen von Hans Kesting und Fredrik Mörk als richtig betrachten werde, aber ich glaube nicht, dass ich der von Raj More implementierten Lösung vertrauen werde.

Ich habe mich auch genau gefragt, welchen Code Raj More geschrieben hätte, da er eine Idee hatte, aber keinen Code zur Verfügung stellte. Ich habe die PointToScreen () -Methode erst vollständig verstanden, als ich diesen Beitrag gelesen habe: http://social.msdn.microsoft.com/Forums/en-US/netfxcompact/thread/aa91d4d8-e106-48d1-8e8a-59579e14f495

Hier ist meine Testmethode. Beachten Sie, dass sich die in den Kommentaren erwähnte Methode 1 geringfügig von der von Hans Kesting unterscheidet.

private Point GetLocationRelativeToForm(Control c)
{
  // Method 1: walk up the control tree
  Point controlLocationRelativeToForm1 = new Point();
  Control currentControl = c;
  while (currentControl.Parent != null)
  {
    controlLocationRelativeToForm1.Offset(currentControl.Left, currentControl.Top);
    currentControl = currentControl.Parent;
  }

  // Method 2: determine absolute position on screen of control and form, and calculate difference
  Point controlScreenPoint = c.PointToScreen(Point.Empty);
  Point formScreenPoint = PointToScreen(Point.Empty);
  Point controlLocationRelativeToForm2 = controlScreenPoint - new Size(formScreenPoint);

  // Method 3: combine PointToScreen() and PointToClient()
  Point locationOnForm = c.FindForm().PointToClient(c.Parent.PointToScreen(c.Location));

  // Theoretically they should be the same
  Debug.Assert(controlLocationRelativeToForm1 == controlLocationRelativeToForm2);
  Debug.Assert(locationOnForm == controlLocationRelativeToForm1);
  Debug.Assert(locationOnForm == controlLocationRelativeToForm2);

  return controlLocationRelativeToForm1;
}

1
private Point FindLocation(Control ctrl)
{
    if (ctrl.Parent is Form)
        return ctrl.Location;
    else
    {
        Point p = FindLocation(ctrl.Parent);
        p.X += ctrl.Location.X;
        p.Y += ctrl.Location.Y;
        return p;
    }
}

0

Das, was ich getan habe, wirkt wie ein Zauber

            private static int _x=0, _y=0;
        private static Point _point;
        public static Point LocationInForm(Control c)
        {
            if (c.Parent == null) 
            {
                _x += c.Location.X;
                _y += c.Location.Y;
                _point = new Point(_x, _y);
                _x = 0; _y = 0;
                return _point;
            }
            else if ((c.Parent is System.Windows.Forms.Form))
            {
                _point = new Point(_x, _y);
                _x = 0; _y = 0;
                return _point;
            }
            else 
            {
                _x += c.Location.X;
                _y += c.Location.Y;
                LocationInForm(c.Parent);
            }
            return new Point(1,1);
        }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.