10

I'm searching for a property which gives me a Control's location relative to its Form's location, not to the Form's ClientRectangle's "0,0".

Of course I can convert everything to screen coordinates, but I wonder if there's a more direct way to do this.

ispiro
  • 26,556
  • 38
  • 136
  • 291
  • What do you exactly mean by _control's location_ is it cursor's location that you want? – uday Apr 19 '12 at 20:41
  • @uDaY I mean: in screen coordinates. If the form is at 100,100 and the control is at 150,350 I want to get 50,250. – ispiro Apr 19 '12 at 20:44
  • Watch out: http://stackoverflow.com/questions/8838621/how-to-determine-actual-windows-form-size-with-all-nonclient-elements-when-run – Hans Passant Apr 19 '12 at 21:18

5 Answers5

14

You need to convert to screen coordinates and then do some math.

Point controlLoc = form.PointToScreen(myControl.Location);

The form's location is already in screen coordinates.

Now:

Point relativeLoc = new Point(controlLoc.X - form.Location.X, controlLoc.Y - form.Location.Y);

That will give you the location relative to the form's upper-left corner, rather than relative to the form's client area.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Thanks for your answer, but as I wrote in the question I'm looking for a more direct way than checking screen coordinates. – ispiro Apr 19 '12 at 20:59
  • 1
    @ispiro: There isn't a more direct way built in. You could easily wrap that logic up into a little function that you can call. Or, when the form is first displayed, you could call `form.PointToScreen(new Point(0, 0))`, save the result, and then just add the X and Y values to the control's `Location` when you want to get your window-relative coordinates. – Jim Mischel Apr 19 '12 at 21:25
  • Regarding my last comment. I meant that you call `PointToScreen` to get the form's client area location, and subtract the X and Y values of the Location to get the offsets. Sorry about that. – Jim Mischel Apr 20 '12 at 00:06
6

I think that this will answer your question. Note that "this" is the form.

Rectangle screenCoordinates = control.Parent.ClientToScreen(control.ClientRectangle);
Rectangle formCoordinates = this.ScreenToClient(screenCoordinates);
Casperah
  • 4,504
  • 1
  • 19
  • 13
  • Thanks for your answer, but as I wrote in the question I'm looking for a more direct way than checking screen coordinates. – ispiro Apr 19 '12 at 21:00
3

It seems that the answer is that there is no direct way to do this.

(As I stated in the question I'm looking for a way other than using screen coordinates.)

ispiro
  • 26,556
  • 38
  • 136
  • 291
1

The selected answer is technically correct given the specifics of the question: such a property does not exist in the .NET framework.

But if you would like such a property, here is a control extension which will do the trick. Yes, it uses screen coordinates, but given the general nature of the post title, I'm sure some users who land on this page may find this useful.

Incidentally, I spent a couple hours trying to do this without screen coordinates by looping through all of the control parents. I could never get the two methods to reconcile. This may well be due to Hans Passant's comment to the OP about how Aero lies about the window size.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Cambia
{
    public static class ControlExtensions
    {
        public static Point FormRelativeLocation(this Control control, Form form = null)
        {
            if (form == null)
            {
                form = control.FindForm();
                if (form == null)
                {
                    throw new Exception("Form not found.");
                }
            }

            Point cScreen = control.PointToScreen(control.Location);
            Point fScreen = form.Location;
            Point cFormRel = new Point(cScreen.X - fScreen.X, cScreen.Y - fScreen.Y);

            return cFormRel;

        }

    }
}
1

None of the answers above helped when you have the control inside of lots of other controls with Autosize set to true, i.e.

Form -> FlowLayoutPanel -> Panel -> Panel -> Control

So I've written my own code with a different logic, I just don't know what would the result be if some docking used among the parent controls. I guess Margins and Paddings will need to get involved in that case.

    public static Point RelativeToForm(this Control control)
    {

        Form form = control.FindForm();
        if (form is null)
            return new Point(0, 0);

        Control parent = control.Parent;

        Point offset = control.Location;            

        while (parent != null)
        {
            offset.X += parent.Left;
            offset.Y += parent.Top;                
            parent = parent.Parent;
        }

        offset.X -= form.Left;
        offset.Y -= form.Top;

        return offset;

    }
Roni Tovi
  • 828
  • 10
  • 21