Solved

Example of ClientToParent to get accurate co-ordinates of an object on a form's canvas.

Posted on 2009-05-04
16
1,338 Views
Last Modified: 2012-05-06
What is the best way to get an object's co-ordinates relative to a parent?
I assume it's ClientToParent? If so can you please provide an example...

So let's say I have a TForm, with a TPanel somewhere on the right side of the form, and on that panel a TButton.
The button's Left property would be 25 for example, but to get it's position relative to the form I'd have to do this:
Button1.Left + Panel1.Left.
Is there an easier way to get the button's left relative to the canvas of the form itself?
I'm trying this:
 FillRect(Rect(ClientToParent(point(Button1.left, Button1.top), form2).X - 2, 0, 100, 100));
..which compiles, but at runtime I get "Project Project1.exe raised exception class EInvalidOperation with message 'Parent given is not a parent of 'Form2''.

FillRect(Rect(ClientToParent(point(Button1.left, Button1.top), form2).X - 2, 0, 100, 100));

Open in new window

0
Comment
Question by:rfwoolf
  • 9
  • 4
  • 3
16 Comments
 
LVL 37

Assisted Solution

by:Geert Gruwez
Geert Gruwez earned 250 total points
ID: 24302041
what about convert the form's topleft clientarea to screencoordinates and also for the component you are interested in ...
then just subtract ...

function TForm1.FormRelative(aWinControl: TWinControl): TPoint;

var P: TWinControl;

begin

  Result := Point(0, 0);

  P := aWinControl;

  while (P <> nil) and (P.Parent <> nil) do

    P := P.Parent;

  if P <> nil then

    Result := Point(aWinControl.ClientOrigin.X - P.ClientOrigin.X, aWinControl.ClientOrigin.Y - P.ClientOrigin.Y);

end;

Open in new window

0
 
LVL 37

Assisted Solution

by:Geert Gruwez
Geert Gruwez earned 250 total points
ID: 24302045
this was a test i used i used to come to that and that's how i found the ClientOrigin property
procedure TForm1.Button2Click(Sender: TObject);

  procedure AddCoord(Msg: string; X, Y: Integer);

  begin

    Memo1.Lines.Add(Format('%s : (%d, %d)', [Msg, X, Y]));

  end;

var FormTopLeft, ControlTopLeft: TPoint;

begin

  FormTopLeft := ClientToScreen(Point(0, 0));

  ControlTopLeft := Button1.ClientToScreen(Point(0, 0));

  AddCoord('Form (Left, Top)', Left, Top);

  AddCoord('Form client (Left, Top)', ClientOrigin.X, ClientOrigin.Y);

  AddCoord('Form screen (Left, Top)', FormTopLeft.X, FormTopLeft.Y);

  AddCoord('Button (Left, Top)', Button1.Left, Button1.Top);

  AddCoord('Button FormRel (Left, Top)', ControlTopLeft.X - ClientOrigin.X, ControlTopLeft.Y - ClientOrigin.Y);

  ControlTopLeft := FormRelative(button1);

  AddCoord('Button FormRel (Left, Top)', ControlTopLeft.X, ControlTopLeft.Y);

end;

Open in new window

0
 
LVL 12

Accepted Solution

by:
Hypo earned 250 total points
ID: 24302476
I think the problem in your code is that you call ClientToParent on the Form itself, and not on the Button...
With this example below, I have a Form, which has a Label and a Panel and on the Panel I have a TButton. Then when I connect the event, I get the correct coordinates of the button relative to the form.
But when I only used ClientToParent, and not Button1.ClientToParent, I got the same error as you did.

regards
Hypo
procedure TForm1.Button1MouseMove(Sender: TObject; Shift: TShiftState; X,  Y: Integer);

var p : TPoint;

begin

  p := Button1.ClientToParent(Point(X, Y), Form1);

  Label1.Caption := IntToStr(p.X)+ ' ' + IntToStr(p.Y);

end;

Open in new window

0
 
LVL 13

Author Comment

by:rfwoolf
ID: 24307356
Thanks guys, both solutions work great, except for one problem...
they are off by the size of the titlebar and possibly the form's border, because for some reason the title bar is part of the form's "client" area (which I don't really agree with but anyway).
So in both cases, I need to be able to adjust the co-ordinates by the height of the titlebar, and the width of form's border.
It's bullshit, I know.
Any ideas on that one?
0
 
LVL 37

Expert Comment

by:Geert Gruwez
ID: 24308094
the ClientOrigin is below the title bar for my delphi 7 app ... i think ... i'll start up my other laptop to check
this new vista sucks ...
0
 
LVL 13

Author Comment

by:rfwoolf
ID: 24308120
lol... I'm using XP.
Anyway I think I'm close to cracking it.
ClientOrigin.X, ClientOrigin.Y : (357, 195)
Screen: Button.left, button.top : (365, 203)
If you take Button1.ClientToScreen(Point(0, 0)) and subtract ClientOrigin.X and  ClientOrigin.Y, I think that gives you the accurate co-ordinates to paint on the form's canvas while compensating for the title bar and borders :p... will confirm in a few moments
0
 
LVL 13

Author Comment

by:rfwoolf
ID: 24308238
No joy. Still doesn't give the height of the title bar - I was confused. I'll keep thinking.
0
 
LVL 12

Expert Comment

by:Hypo
ID: 24308263
In Vista it works fine, but when I tried it on XP earlier, I think I got the same result as you do now... that's strange... :/
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 13

Author Comment

by:rfwoolf
ID: 24308335
I suppose one workaround is to use the API to get the titlebar height - but I'm loathed to use it - there must be a native way. You do understand the problem, right?
Here's an overview in case you don't follow...
If I paint on the form canvas  as in MyCanvas.Handle := Button1.Parent.Handle;
And I output to 0,0, it will paint at the very top left of the title bar, not on the client area.
I've managed to measure my title bar at about 31 pixels high, with the border being 4 pixels wide.

So then, how do we find the point of the form's client area, not including the title bar and border?
0
 
LVL 13

Author Comment

by:rfwoolf
ID: 24308391
Here's another idea...
Given that a form's height and width differ from its clientheight and clientwidth by the width of the title bar and border, we could use this as a workaround - however I guess one would have to test it on different systems.
Sigh... this sucks
0
 
LVL 12

Expert Comment

by:Hypo
ID: 24308412
Could you try if this works (using MapWindowPoints instead)? It works for me in Vista, but so did also the previous solution... :/

regards
Hypo
procedure TForm1.Button1MouseMove(Sender: TObject; Shift: TShiftState; X,  Y: Integer);

var p : TPoint;

begin

  p := Point(X, Y);

  MapWindowPoints(Button1.Handle, Form1.Handle, p, 1);

  Label1.Caption := IntToStr(p.X)+ ' ' + IntToStr(p.Y);

end;

Open in new window

0
 
LVL 13

Author Comment

by:rfwoolf
ID: 24308485
Hypo,
This solution also "works" as did the previous one, but again, if painting on the form's "canvas", the co-ordinates of your method do not compensate for the height of the title bar or borders.
For example, if I put a button at the top-left of my form, button.left = 0, button.top = 0;
Your procedure will generate 0,0.
If I paint to 0,0 it will paint onto the title bar :(
0
 
LVL 13

Author Comment

by:rfwoolf
ID: 24308556
LOL
You know my "workaround"  where the height of the taskbar is Form.Height - Form.ClientHeight, and the width of the border is Form.Width - Form.ClientWidth???
Well that won't work either. With the width it will give the width of 2 borders, right? So you halve it, no problem.
But the height includes the titlebar PLUS the bottom border.
You could assume that the height of the bottom border is the same as the width of the left or right borders, but it doesn't give me much confidence.
Sigh...
0
 
LVL 12

Expert Comment

by:Hypo
ID: 24308584
I think I misunderstood your problem earlier... but now i get it, It's not the coordinates itself that are wrong, it's the fact that painting that's wrong; Anyway... I think I remember that there are some Win32 API routines you can use to get the correct client area of your window... give me a few minutes and I might find them for you... :)
0
 
LVL 13

Author Comment

by:rfwoolf
ID: 24308609
That would be great.
Now that I look at it, technically this question is answered - you guys showed me how to use ClientToParent and you also showed me another way of finding an objects co-ords relative to the form.
I think I will open a new question.
0
 
LVL 13

Author Closing Comment

by:rfwoolf
ID: 31577781
Thanks guys.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

929 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now