Solved

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

Posted on 2009-05-04
16
1,380 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

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

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…
How to Install VMware Tools in Red Hat Enterprise Linux 6.4 (RHEL 6.4) Step-by-Step Tutorial

751 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