By Greg McGuffey - 10/4/2007
I'm having a heck of a time figuring this out.
I have a control that is within a tab, that is in toostripcontainer. I want to programmatically place a control (a label) such that the upper left corner of the label is right below the controls lower left corner. Now this is trivial if they are both in the same container, in this case the tab page. Alas, that is not the case. The label is in the form's control collection, not the tabs.
My first try was to get the point that is at the lower left of the control in the tab, translate it to screen coordinates, then translate it back to client coordinates, but relative to the form:
Dim lowerLeftClient As Location = New Location(theControl.Left, theControl.Top + theControl.Height)
Dim lowerLeftScreen As Location = theControl.PointToScreen(lowerLeftClient)
Dim lowerLeftForm As Location = theControl.ParentForm.PointToClient(lowerLeftScreen)
theLabel.Location = lowerLeftForm
This typically results in the label being too low and too far right.
Then I thought maybe the PointToClient relative to the form was including the borders and header, but when setting the location, these were not. So I attempted to take this into consideration by comparing the Size of the form to the ClientSize. I figured the X difference \ 2 should be the left border width (or close) and maybe the Y difference minus the border width would be the header height. Alas, this degraded into finding magic numbers...which changed on every form/layout.
So, if you've followed this far, I'm hoping you can help me figure out how to retrieve a location relative to the form itself, of a control buried within other control(s).
Thanks!
|
By Peter Denton - 10/7/2007
G'dayI believe that the location of a control is relative to its parent control/container, so it would seem that you have to accumulate the locations of the control you want to label with that of it's parent, and its parent's parent until you get back to the form. Then you have to subtract the accumulation of the locations of the parent of control/container that will contain the label, and its parent etc. Then after allowing for the size of the label (or should that be the size of the control being labeled, is the location top left or bottom left?) and the gap, this should give you the offset you want. This should be a completely general solution. Please forgive me if I'm telling you something you had already figured out. Peter P.S. A wise man once said "For every complex problem there is a simple and elegent solution that's wrong". I hope I'm not proposing that solution.
|
By StrataFrame Team - 10/8/2007
Yep, Peter is right... Your best bet would be to recursively add the .Location of each parent up the chain until you get to the form. If you are off a few pixels, then you might need to take into consideration the .BorderStyle property of the parent controls. For instance, setting a Panel's BorderStyle to FixedSingle shifts everything by 1 pixel, while setting it to Fixed3D shifts it by 2 pixels, so you would need to offset your values by the totals of the pixels (yeah, pain in the posterior).
|
By StrataFrame Team - 10/8/2007
Also, my testing on Me.PointToClient(Me.PointToScreen(somePoint)) worked correctly (without worrying about the borders). However, the thing to remember would be that you have to call PointToScreen() on the parent of the nested control and then PointToClient on the form. So, from the form:Me.PointToClient(Me.ParentPanel.PointToScreen(control.BottomLeft)) would give you the coordinates you're looking for (with substitution as necessary ).
|
By Greg McGuffey - 10/9/2007
Thanks guys! I had accidentally found a work around, but after working through the comments I figured out where I went wrong.
The key was to do the PointToScreen relative to the Parent of my control. I was doing it relative to the control itself. The code looked pretty much like Ben suggested (glad I didn't need to do the recursive thing ). Here is the comparison the the wrong and right way to do it, relative to my custom control:
WRONG (works correctly only if the Parent is the ParentForm, but messes up if Parent is another control)
Me.ParentForm.PointToClient(Me.PointToScreen(New Point(Me.Left, Me.Top + Me.Height)))
RIGHT (works in all cases)
Me.ParentForm.PointToClient(Me.Parent.PointToScreen(New Point(Me.Left, Me.Top + Me.Height)))
Thanks again
|
By StrataFrame Team - 10/11/2007
Woot, glad you got it
|
By Matt Searles - 12/5/2007
I was having a similar problem, in that I wanted to place a borderless form just under a label when it was clicked, and couldn't get the true screen co-ordinates of the control. Here's a recursive method as suggested by Peter to do that. It doesn't take into account borderwidths, but it gives the location of the control on the screen. Point AddPoints(Point p1, Point p2){ return new Point(p1.X + p2.X, p1.Y + p2.Y);} Point SubtractPoints(Point p, Point from){ return new Point(from.X - p.X, from.Y - p.Y);} private void ScreenLocation(Control ctl, ref Point screenLocation){ screenLocation = AddPoints(ctl.Location, screenLocation); if (ctl.Parent != null){ ScreenLocation(ctl.Parent, ref screenLocation);} else{ screenLocation = PointToScreen(SubtractPoints(ctl.Location, screenLocation)); } }
|
By StrataFrame Team - 12/6/2007
I had to do this just the other day, and I ended up doing it like this:To find the bottom left corner: Point pt = this.myLabel.PointToScreen( new Point( 0, this.myLabel.Height ) ); BorderlessForm f = new BorderlessForm( ); f.StartPosition = FormStartPosition.Manual; f.Location = pt; f.Show( ); Maybe that will help. It gets the screen coordinates with respect to the label (since the method was called on the label), so you need the left edge with respect to the label (0), and you need the bottom edge with respect to the label (Height).
|
|