【问题标题】:Transparency of picture box图片框透明度
【发布时间】:2019-01-19 16:28:17
【问题描述】:

我只是在我的简单问题中寻找答案。在这里

我有一个带有透明background 图像的图片框我将图片框backcolor设置为透明。

然后,图片透明bg。但是在我添加了这段代码之后

ìmg1.Left = windows.forms.cursor.Position.X - me.Left ìmg1.Top= windows.forms.cursor.Position.Y - me.Top

'code for using img as cursor

图像bg不是像这样透明的

我认为透明的backcolor并不是真正透明的。它只会获取backcolorof 形式并将其用作backcolorof 图像而不是透明的。

有没有办法让它完全透明?

【问题讨论】:

  • 图片文件类型是什么?
  • 如果你用 GDI+ 画成矩形,你会避开不透明的控件的后部。
  • png 并且图片的背景是透明的

标签: vb.net winforms


【解决方案1】:

您的假设是正确的。
winforms中的透明并不意味着对象实际上是透明的。相反,这意味着它将显示它的父对象而不是它的背景,包括它的背景、图像和文本,但不包括它上面的任何其他控件,因此你的问题。
由于您最顶部的图片框的父控件不是也不能是其他图片框,因此您的最顶部的图片框具有透明背景这一事实将无济于事。

不幸的是,使用表单的TransparencyKey 属性也无济于事。 (它会使所选颜色透明,但会产生意想不到的(通常是不希望的)结果。

为了达到你的目的,你必须在cmets中听从OneFineDay的建议,并使用Graphics自己绘制图像。
幸运的是,这很容易做到:

Public Sub DrawImage(Image as Image, Location As Point)
    Using(Dim g as Graphics = Me.CreateGraphics())
        g.DrawImage(Image, Location)
    EndUsing
End Sub

【讨论】:

  • 设置TransparencyKey 可能会产生不良结果,但“意外”似乎是错误的词?该设置的结果是可知的,并且对于任何熟悉该设置的人来说都应该是显而易见的。
  • 不是根据我对这个案例的测试。自己尝试一下,您就会明白我的意思。我使用了一个背景颜色与透明度键相同的图片框,部分覆盖了另一个图片框。结果不稳定——这意味着每次我运行项目时,都会得到不同的显示。
  • 嗯。完全出乎意料。我撤回我的评论。
【解决方案2】:

这个blog article 启发了这个SO answer。这些是通过缩放、文本、内容对齐等进行更强大控制的基础。

以下是缩小版本(在 VB 中),主要实现真正透明的外观。核心绘画几乎与original SO post 相同,只是在绘画中考虑了边框。还保留了一些控制级别的功能。

'Namespace omitted to reduce indentation
Imports System.Windows.Forms
Imports System.Drawing
Imports System.ComponentModel
Imports System.Drawing.Drawing2D

Public Class TransPicBox
    Inherits PictureBox

    Public Enum ImageSizing
        None
        Stretch
        Scale
    End Enum

    Public Sub New()
        ' defaults for a new one
        MyBase.BackColor = Color.Transparent
        MyBase.InitialImage = Nothing
        MyBase.ErrorImage = Nothing
        MyBase.Image = Nothing

    End Sub

    Public Overloads Property Image As Image
        Get
            Return MyBase.Image
        End Get
        Set(value As Image)
            MyBase.Image = value
            InvalidateParent()
        End Set
    End Property

    Private imgSizing As ImageSizing = ImageSizing.None
    Public Property ImageSizing As ImageSizing
        Get
            Return imgSizing
        End Get
        Set(value As ImageSizing)
            imgSizing = value
            InvalidateParent()
        End Set
    End Property

    ' because the child control displays are interdependent
    ' tell the parent to update when some things change
    ' Image, Scaling, Border, Text, BackColor etc
    Private Sub InvalidateParent()
        Invalidate()
        If MyBase.Parent IsNot Nothing Then
            MyBase.Parent.Invalidate()
        End If
    End Sub

    ' since the display depends on ZOrder, provide
    ' a control method to alter it
    Public Sub MoveUpZOrder() 
        ChangeZOrder(-1)
    End Sub

    Public Sub MoveDownZOrder() 
        ChangeZOrder(+1)
    End Sub

    Private Sub ChangeZOrder(value As Int32)
        Dim ndx As Integer = Parent.Controls.GetChildIndex(Me)

        If ((ndx + value) >= 0) AndAlso ((ndx + value) < Me.Parent.Controls.Count) Then
            Me.Parent.Controls.SetChildIndex(Me, ndx + value)
        End If
    End Sub

    ' if you want to remove properties, this is how
    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)>
    Public Shadows Property ErrorImage As Image

    Protected Overrides Sub OnPaintBackground(pevent As PaintEventArgs)
        If MyBase.BackColor = Color.Transparent Then
            ' magic happens here!
            PaintSiblings(pevent)
        Else
            ' do nothing special when the backcolor is not Transparent
            MyBase.OnPaintBackground(pevent)
        End If

    End Sub

    ' code for painting the image
    Protected Overrides Sub OnPaint(pe As PaintEventArgs)
        Dim rect As Rectangle

        If (MyBase.Image IsNot Nothing) Then
            rect = GetImgRect(Bounds)
            pe.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
            pe.Graphics.CompositingQuality = CompositingQuality.HighQuality
            pe.Graphics.SmoothingMode = SmoothingMode.HighQuality
            pe.Graphics.DrawImage(Image, rect)
        End If
    End Sub

    Private Sub PaintSiblings(e As PaintEventArgs)
        ' need to access the parent' controls collection 
        If (Parent IsNot Nothing) Then
            Dim borderSize As Integer = 0
            Dim thisLeft As Single = -Left
            Dim thisTop As Single = -Top

            ' fix
            Select Case MyBase.BorderStyle
                Case BorderStyle.FixedSingle
                    borderSize = SystemInformation.BorderSize.Width
                Case BorderStyle.Fixed3D
                    borderSize = SystemInformation.Border3DSize.Width
            End Select

            ' Shift ClipBounds to form relative coords
            e.Graphics.TranslateTransform(thisLeft, thisTop)

            ' Get Parent to paint the part behind us:
            ' we cant know if thats been done or not
            Using pea As New PaintEventArgs(e.Graphics, e.ClipRectangle)
                InvokePaintBackground(Parent, pea)
                InvokePaint(Parent, pea)
            End Using

            ' shift back
            e.Graphics.TranslateTransform(-thisLeft, -thisTop)

            ' starting control index is...well, ours
            Dim startAt As Integer = Parent.Controls.GetChildIndex(Me)
            Dim ctl As Control

            ' Controls are in z-Order, so loop 
            ' thru the controls "behind" me
            For n As Int32 = Parent.Controls.Count - 1 To startAt + 1 Step -1
                ctl = Parent.Controls(n)

                ' skip if they are invisible, too small or do not overlap me
                If (ctl.Visible = False OrElse 
                            ctl.Width = 0 OrElse 
                            ctl.Height = 0 OrElse
                            Bounds.IntersectsWith(ctl.Bounds) = False) Then
                    Continue For
                Else

                    Using bmp As New Bitmap(ctl.Width, ctl.Height, e.Graphics)
                        ' draw this sibling to a bitmap
                        ctl.DrawToBitmap(bmp, New Rectangle(0, 0, ctl.Width, ctl.Height))

                        ' shift the orientation relative to sibling and draw it
                        thisLeft = ctl.Left - Left
                        thisTop = ctl.Top - Top

                        'offset, then draw the image, reset
                        e.Graphics.TranslateTransform(thisLeft - borderSize, 
                                                thisTop - borderSize)
                        e.Graphics.DrawImageUnscaled(bmp, 
                                                New Point(0, 0))
                        e.Graphics.TranslateTransform(-thisLeft + borderSize, 
                                               -thisTop + borderSize)

                    End Using
                End If
            Next
        Else
            ' not sure how this could happen
            Using br As New SolidBrush(MyBase.BackColor)
                e.Graphics.FillRectangle(br, ClientRectangle)
            End Using
        End If

    End Sub

    ' image scaling is mainly a matter of the size and location
    ' of the img rect we use in Paint
    Private Function GetImgRect(destRect As Rectangle) As Rectangle
        Dim pt As New Point(0, 0)
        Dim sz As Size

        If MyBase.Image IsNot Nothing Then
            Select Case Me.ImageSizing
                Case ImageSizing.None
                    sz = Image.Size

                Case ImageSizing.Scale
                    If Width > Height Then
                        sz = New Size(GetScaledWidth(Height), Height)
                    Else
                        sz = New Size(Width, GetScaledHeight(Width))
                    End If

                Case ImageSizing.Stretch
                    sz = Me.Size
            End Select
        End If
        ' ToDo: move the pt if you add an Image ContentAlignment
        ' (Top, TopLeft, BottomRight...) property
        Return New Rectangle(pt, sz)

    End Function

    Private Function GetScaledWidth(h As Integer) As Integer
        Dim scale As Single = CSng(Image.Width / Image.Height)
        Return CInt(h * scale)
    End Function

    Private Function GetScaledHeight(w As Integer) As Integer
        Dim scale As Single = CSng(Image.Height / Image.Width)
        Return CInt(w * scale)
    End Function
End Class

如何使用它

  1. 创建类库
  2. 用上面的替换新的类样板代码
  3. Imports 语句中列出的命名空间添加引用
  4. 构建库。工具箱中应该会显示一个新的TransPicBox

您也可以只在项目中包含类代码文件并重新构建以避免仅包含一件事的 DLL 依赖项。结果:

  • 一个较大的PNG下有四个小PNG;所有人都在面板上(玫瑰 BG)
  • TopLeft 和 BottomRight 图像使用透明背景,其他 2 个不使用
  • 它们都打开了边框以显示客户区; BL 时钟使用 3D 边框
  • 较大的 PNG 使用 ImageSizing.Scale 大约大 150%

父背景颜色通过 TL 和 BR 图像以及前面重叠的较大图像显示。当控件的背景颜色为透明时,控件将正常显示 BMP 和 JPG,并且仍然显示空白区域(如果有)后面的内容。

注意事项:

  1. 这是一个相当昂贵的Paint。每次需要绘制其中一个控件时,父控件及其下的每个控件的至少一部分也必须重新绘制。
  2. 在窗体设计器中移动TransPicBox 时,VS 会暂时将控件移到最前面,因此显示会暂时被打乱
  3. Windows 会正常启动,因此您的特殊控制背后的东西就它而言仍然是隐藏的。下图显示当鼠标悬停在TransPicBox 后面的按钮部分不会“发光”。据 Windows 所知,它的那部分是看不到的,因此不会重新绘制。

【讨论】:

    【解决方案3】:

    如果要将图形对象用作光标,建议使用图形对象绘制图像。但是,如果您有时想使用 PictureBox(出于能够使用它的 Image 属性快速更改图像等原因),那也是可能的。

    此代码将绘制一个更好的“透明”背景,方法是在您的 PictureBox 后面绘制每个控件的背景。

    使用方法:

    1) 创建自定义类。

    2) 将Inherits PictureBox 放在Public Class ... 行下方。

    3) 将此代码粘贴到类中:

    Protected Overrides Sub OnPaintBackground(e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaintBackground(e)
    
        If Parent IsNot Nothing Then
            Dim index As Integer = Parent.Controls.GetChildIndex(Me)
    
            For i As Integer = Parent.Controls.Count - 1 To index + 1 Step -1
                Dim c As Control = Parent.Controls(i)
                If c.Bounds.IntersectsWith(Bounds) AndAlso c.Visible = True Then
                    Dim bmp As New Bitmap(c.Width, c.Height, e.Graphics)
                    c.DrawToBitmap(bmp, c.ClientRectangle)
                    e.Graphics.TranslateTransform(c.Left - Left, c.Top - Top)
                    e.Graphics.DrawImageUnscaled(bmp, Point.Empty)
                    e.Graphics.TranslateTransform(Left - c.Left, Top - c.Top)
                    bmp.Dispose()
                End If
            Next
        End If
    End Sub
    

    4) 构建您的项目。

    5) 从工具箱中选择您的类并将其添加到您的表单/用户控件中。

    【讨论】:

      猜你喜欢
      • 2014-08-13
      • 2012-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多