Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1418
  • Last Modified:

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

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
rfwoolf
Asked:
rfwoolf
  • 9
  • 4
  • 3
3 Solutions
 
Geert GruwezOracle dbaCommented:
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
 
Geert GruwezOracle dbaCommented:
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
 
HypoCommented:
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
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
rfwoolfAuthor Commented:
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
 
Geert GruwezOracle dbaCommented:
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
 
rfwoolfAuthor Commented:
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
 
rfwoolfAuthor Commented:
No joy. Still doesn't give the height of the title bar - I was confused. I'll keep thinking.
0
 
HypoCommented:
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
 
rfwoolfAuthor Commented:
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
 
rfwoolfAuthor Commented:
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
 
HypoCommented:
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
 
rfwoolfAuthor Commented:
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
 
rfwoolfAuthor Commented:
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
 
HypoCommented:
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
 
rfwoolfAuthor Commented:
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
 
rfwoolfAuthor Commented:
Thanks guys.
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

  • 9
  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now