72

Is there any way to retrieve a control's position in a form, when the control may be inside other controls (like Panels)?

The control's Left and Top properties gives me only it's position within it's parent control, but what if my control is inside five nested panels, and I need it's position on the form?

Quick example:

The button btnA is located on coordinates (10,10) inside the panel pnlB.
The panel pnlB is located on coordinates (15,15) inside the form frmC.

I want btnA's location on frmC, which is (25,25).

Can I get this location?

Sean Hanley
  • 5,677
  • 7
  • 42
  • 53
Erlend D.
  • 3,013
  • 6
  • 37
  • 59

9 Answers9

99

I usually combine PointToScreen and PointToClient:

Point locationOnForm = control.FindForm().PointToClient(
    control.Parent.PointToScreen(control.Location));
Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • 9
    This is the `REAL ABSOULUTE POSITION` http://stackoverflow.com/questions/4998076/getting-the-location-of-a-control-relative-to-the-entire-screen – PUG Mar 16 '12 at 19:54
  • 4
    How does this differ from `control.PointToScreen(Point.Empty);`? – strongriley Jul 03 '12 at 14:29
  • 1
    @strongriley No idea (never tried it and no dev env available right now), but I can't see that behavior mentioned in the documentation, and if so there is no guarantee that it will function in future framework releases. – Fredrik Mörk Jul 03 '12 at 18:20
  • 5
    @strongriley `control.PointToScreen(Point.Empty)` gives location with respect to screen, while the answer gives the location with repsect to top level form. – nawfal Sep 01 '12 at 10:43
  • hey, you helped me a lot with that :) it centers designed popups on center of program's display instead of on the center of all screens! – kokbira Nov 19 '12 at 19:43
  • There seems to be some confusion in the comments. To help future readers, I will say this: `control.Parent.PointToScreen(control.Location)` is the same as `control.PointToScreen(Point.Empty)`; they both return the location of the control relative to the screen (I prefer the second one because it's shorter). Then, we pass that value to `Form.PointToClient()` to get the location relative to the form. – 41686d6564 stands w. Palestine Aug 26 '20 at 19:05
11

I usually do it like this.. Works every time..

var loc = ctrl.PointToScreen(Point.Empty);
noisyass2
  • 580
  • 4
  • 13
11

You can use the controls PointToScreen method to get the absolute position with respect to the screen.

You can do the Forms PointToScreen method, and with basic math, get the control's position.

Raj More
  • 47,048
  • 33
  • 131
  • 198
  • That's not going to work exactly right, unless you take into account the height of the titlebar and the width of the form's border. – MusiGenesis Sep 25 '09 at 15:47
  • How does this help for getting the control's absolute position? You have to pass in a Point object to PointToScreen(), which doesn't make sense in this case. – Tim Oct 13 '09 at 20:33
  • @Tim, you have to pass System.Drawing.Point.Empty in this case: var absolutePosition = control.PointToScreen(System.Drawing.Point.Empty); – Umar T. Oct 30 '17 at 08:22
7

You could walk up through the parents, noting their position within their parent, until you arrive at the Form.

Edit: Something like (untested):

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;
}
Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
  • Yea, I thought of that, but it seemed like a impractical way to do it, so I was hoping there was another way. If no one has any other suggestion, that's what I will do. – Erlend D. Sep 25 '09 at 15:39
  • This is the way to do it, with a simple recursive function. – MusiGenesis Sep 25 '09 at 15:48
5

Supergeek, your non recursive function did not producte the correct result, but mine does. I believe yours does one too many additions.

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

Oddly enough, PointToClient and PointToScreen weren't ideal for my situation. Particularly because I'm working with offscreen controls that aren't associated with any form. I found sahin's solution the most helpful, but took out recursion and eliminated the Form termination. The solution below works for any of my controls visible or not, Form-contained or not, IContainered or not. Thanks 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;
}
BoeroBoy
  • 1,094
  • 11
  • 17
2
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;
    }
}
thejoshwolfe
  • 5,328
  • 3
  • 29
  • 21
sahin
  • 21
  • 1
1

In my testing, both Hans Kesting's and Fredrik Mörk's solutions gave the same answer. But:

I found an interesting discrepancy in the answer using the methods of Raj More and Hans Kesting, and thought I'd share. Thanks to both though for their help; I can't believe such a method is not built into the framework.

Please note that Raj didn't write code and therefore my implementation could be different than he meant.

The difference I found was that the method from Raj More would often be two pixels greater (in both X and Y) than the method from Hans Kesting. I have not yet determined why this occurs. I'm pretty sure it has something to do with the fact that there seems to be a two-pixel border around the contents of a Windows form (as in, inside the form's outermost borders). In my testing, which was certainly not exhaustive to any extent, I've only come across it on controls that were nested. However, not all nested controls exhibit it. For example, I have a TextBox inside a GroupBox which exhibits the discrepancy, but a Button inside the same GroupBox does not. I cannot explain why.

Note that when the answers are equivalent, they consider the point (0, 0) to be inside the content border I mentioned above. Therefore I believe I'll consider the solutions from Hans Kesting and Fredrik Mörk to be correct but don't think I'll trust the solution I've implemented of Raj More's.

I also wondered exactly what code Raj More would have written, since he gave an idea but didn't provide code. I didn't fully understand the PointToScreen() method until I read this post: http://social.msdn.microsoft.com/Forums/en-US/netfxcompact/thread/aa91d4d8-e106-48d1-8e8a-59579e14f495

Here's my method for testing. Note that 'Method 1' mentioned in the comments is slightly different than Hans Kesting's.

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;
}
Tyler Collier
  • 11,489
  • 9
  • 73
  • 80
0

This is what i've done works like a charm

            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);
        }
shiroxx
  • 177
  • 1
  • 2