Reputation: 514
I've been through quite a number of posts about adding a Label over a PictureBox, and none of the ones I've seen explain why my label disappears when over a PictureBox.
I created a completely new Label from scratch with the same problem, to rule out that perhaps I inadvertently changed some property without realizing it.
When the Form first loads, the PictureBox has no initial image set.
At this time, the Label is visible, but it's Paint
event isn't called.
When an Imaged is assigned to the PictureBox, through a Button click, the Label is no longer visible and the Paint event still isn't called.
However, Paint events for another Label, outside of the PictureBox, are called.
In the designer I see this:
Me.tabDesign.Controls.Add(Me.lblTitleTest)
Thus, following this post and this one, I do this:
Me.lblTitleTest.Location = Me.picMainImage.PointToClient(Me.tabDesign.PointToScreen(lblTitleTest.Location))
Me.lblTitleTest.Parent = Me.picMainImage
But it doesn't have any effect.
Next, I tried using the Paint event (below), but the event is only called for Labels outside of the PictureBox.
The Paint event is not called if the Label is positioned over top of the PictureBox:
Private Sub DrawTestTitleText(sender As Object, e As PaintEventArgs) Handles lblTitleTest.Paint
MsgBox("Drawing new test title")
TextRenderer.DrawText(e.Graphics, "Test Title", lblTitleTest.Font, New Point(10, 10), Color.Black)
End Sub
If I use this same code with a different Label, not over the PictureBox, the draw event fires as expected.
As a test, I tried to assign an Image from the Project's Resources to the PictureBox. That didn't help: the Label wasn't ever visible at all and its Paint event never triggered.
There must be some subtle nuance here there I'm missing.
I'm targeting .NET Framework 4.6.1.
Upvotes: 1
Views: 357
Reputation: 32278
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:
Upvotes: 2