Solved

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

Posted on 2009-05-04
16
1,335 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 36

Assisted Solution

by:Geert Gruwez
Geert Gruwez earned 250 total points
Comment Utility
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 36

Assisted Solution

by:Geert Gruwez
Geert Gruwez earned 250 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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 36

Expert Comment

by:Geert Gruwez
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 13

Author Comment

by:rfwoolf
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Thanks guys.
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

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 this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

744 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