Relocate a Control to a different Parent container maintaining the relative position:
Assume, as in the graphic sample, that the PictureBox.Location
is (10, 100)
. In the Form Designer, a Label is positioned over the PictureBox, its Location is (20, 110)
.
Its position, relative to the PictureBox.ClientRectangle
, is then (10, 10) => (20-10, 110-100)
).
Of course, as shown, the Label's background color is inherited from its Parent, the Form (transparency of Controls is virtual, the Parent's background is used as the background of child Control that have a transparent BackColor).
▶ The PictureBox control is not a ContainerControl or a ScrollableControl (it doesn't implement IContainerControl; i.e., doesn't have the WS_EX_CONTROLPARENT
style set), so it doesn't host a Control when you place one over its surface at design-time.
At run-time, you can set a Control's Parent
property to a PictureBox reference, to create a new Parent-Child relation between these two Controls.
When you do, some properties related to a Control's layout need to be considered; in this case the Location
property.
Setting the Parent doesn't change the child Control's Location
value, which is now relative to the new Parent.
▶ The Label.Location
is still (20, 110)
, but now this position is related to the PictureBox.ClientRectangle
, not the Form's. As a consequence, you have to relocate it, if you want it to keep its relative position.
You can subtract the new Parent's Location from the child Control's Location
:
[Label].Parent = [PictureBox]
[Label].Location = Point.Subtract([Label].Location, New Size([PictureBox].Left, [PictureBox].Top))
Or transform the Labels's client coordinates in Screen coordinates, then apply the Screen coordinates to the Parent's client coordinates:
[Label].Location = [PictureBox].PointToClient([Label].PointToScreen(Point.Empty))
When the Label is set to a new Parent container, its background is also adapted to the new Parent's background, to preserve the apparent transparency.
In Form.Load
(assume the Label is named label1
and the PictureBox pictureBox1
):
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
label1.Parent = pictureBox1
label1.Location = pictureBox1.PointToClient(label1.PointToScreen(Point.Empty))
' Or
' label1.Location = Point.Subtract(label1.Location, New Size(pictureBox1.Left, pictureBox1.Top))
End Sub
Design-Time: Run-Time:

You can do the exact same thing drawing the text directly on the PictureBox surface, in the same position, defining the drawing Rectangle where the Text will be drawn, the same position and size as the Label you previously used.
NOTE:
Here, I'm using the the PictureBox Font
property as the Font
parameter of TextRenderer.DrawText. The PictureBox hides this property, but it's still available (belongs to the Control
class); it's inherited from its Parent, Form or other container. You can of course specify any other Font.
See the TextRenderer
's TextFormatFlags, to place the Text in other positions (e.g., Top/Left).
Here, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter
are used to center the text in the middle of the drawing box, defined by drawingRect
in the Paint event.
private canvasFlags as TextFormatFlags = TextFormatFlags.HorizontalCenter Or
TextFormatFlags.VerticalCenter Or TextFormatFlags.NoPadding
private canvasText as String = "Some Text just for show"
private sub picCanvas_Paint(sender As object, e As PaintEventArgs) Handles pictureBox1.Paint
Dim pBox = DirectCast(sender, PictureBox)
Dim drawingRect = new Rectangle(10, 10, pBox.Width - 20, pBox.Font.Height + 4)
TextRenderer.DrawText(e.Graphics, canvasText, pBox.Font, drawingRect, Color.White, Color.Empty, canvasFlags)
End Sub
At Run-Time, Invalidate() the PictureBox when you want to repaint it to, e.g., replace the Text.
Resulting in:
