当 Image 分配给其下的 PictureBox 时标签消失

Label disappears when an Image is assigned to a PictureBox under it

我看过很多关于在 PictureBox 上添加标签的帖子,none 我看过的帖子解释了为什么我的标签在 PictureBox 上消失。
我用同样的问题从头开始创建了一个全新的标签,以排除我可能在没有意识到的情况下无意中更改了一些 属性。

首次加载窗体时,PictureBox 没有设置初始图像。
此时Label是可见的,但是Paint事件没有被调用。

当 Imaged 被分配给 PictureBox 时,通过单击 Button,Label 不再可见并且仍然不会调用 Paint 事件。

但是,会调用 PictureBox 之外的另一个 Label 的 Paint 事件。

在设计器中我看到了这个:

Me.tabDesign.Controls.Add(Me.lblTitleTest)

因此,在 this post and this one 之后,我这样做:

Me.lblTitleTest.Location = Me.picMainImage.PointToClient(Me.tabDesign.PointToScreen(lblTitleTest.Location))
Me.lblTitleTest.Parent = Me.picMainImage

但是没有任何效果

接下来,我尝试使用 Paint 事件(如下),但该事件仅针对 PictureBox 外部 的标签调用。
如果 Label 位于 PictureBox 的顶部,则不会调用 Paint 事件:

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

如果我将相同的代码与不同的标签一起使用,而不是在 PictureBox 上,绘制事件会按预期触发。

作为测试,我尝试将项目资源中的图像分配给 PictureBox。这没有帮助:Label 根本不可见,它的 Paint 事件也从未触发。

这里一定有一些细微的差别,我错过了。

我的目标是 .NET Framework 4.6.1。

将控件重新定位到保持相对位置的不同父容器:

假设,如在图形示例中,PictureBox.Location(10, 100)。在 Form Designer 中,一个 Label 位于 PictureBox 之上,它的 Location 是 (20, 110).
它相对于 PictureBox.ClientRectangle 的位置是 (10, 10) => (20-10, 110-100)).

当然,如图所示,Label的背景颜色是继承的父窗体(控件的透明度是虚拟, Parent 的背景用作具有透明 BackColor 的子 Control 的背景。

▶ PictureBox 控件不是 ContainerControl or a ScrollableControl (it doesn't implement IContainerControl; i.e., doesn't have the WS_EX_CONTROLPARENT 样式集),因此当您在设计时将一个控件放置在其表面上时,它不会承载控件。

在运行时,您可以将控件的Parent 属性设置为PictureBox引用,以在这两个控件之间创建新的父子关系。
当您这样做时,需要考虑一些与控件布局相关的属性;在这种情况下 Location 属性.
设置父级不会更改子控件的 Location 值,该值现在是相对于新父级的。

Label.Location 仍然是 (20, 110),但现在这个位置与 PictureBox.ClientRectangle 有关,而不是 Form 的。因此,如果您希望它保持其相对位置,则必须重新定位它。

  1. 您可以从子控件的Location:

    中减去新父控件的位置
    [Label].Parent = [PictureBox]
    [Label].Location = Point.Subtract([Label].Location, New Size([PictureBox].Left, [PictureBox].Top))
    
  2. 或者将Labels的客户坐标转换为Screen坐标,然后将Screen坐标应用到Parent的客户坐标:

    [Label].Location = [PictureBox].PointToClient([Label].PointToScreen(Point.Empty))
    

当 Label 设置为新的 Parent 容器时,其背景也会适应 以适应新的 Parent 的背景,以保持明显的透明度。

Form.Load中(假设Label命名为label1,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:

您可以执行完全相同的操作,直接在 PictureBox 表面上绘制文本,在相同的位置,定义将在其中绘制文本的绘图矩形,与您之前使用的标签的位置和大小相同。

注意:
在这里,我使用 PictureBox Font 属性 作为 TextRenderer.DrawTextFont 参数。 PictureBox 隐藏了这个 属性,但它仍然可用(属于 Control class);它继承自它的 Parent、Form 或其他容器。您当然可以指定任何其他字体。

参见 TextRendererTextFormatFlags,将文本放置在其他位置(例如,Top/Left)。
这里,TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter用于将文本居中放置在绘图框的中间,由Paint事件中的drawingRect定义。

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

在 运行 时,Invalidate() PictureBox 当您想要重新绘制它时,例如,替换 Text。
结果: