Transparency of picture box

后端 未结 3 1171
被撕碎了的回忆
被撕碎了的回忆 2020-12-04 04:08

Im just looking for an answer in my simple problem. Here it is

I have a pricturebox that has image with transparent background i Set the picturebox

相关标签:
3条回答
  • 2020-12-04 04:24

    You are correct in your assumption.
    Transparency in winforms does not mean that the object is actually transparent. Instead, it means that it will display it's parent object instead of it's background, including it's background, images and text, but not including any other controls on it, hence your problem.
    Since the parent control of your top most picture box is not and can not be the other picture boxes, the fact that your top most picture box have a transparent background will not help.

    Unfortunately, using the form's TransparencyKey property will also not help for this. (It will make the selected color transparent, but will yield unexpected (and usually undesired) results.

    In order to achieve your goal, you will have to follow OneFineDay's advice in the comments, and use Graphics to draw the image yourself.
    Fortunately, this is very easy to do:

    Public Sub DrawImage(Image as Image, Location As Point)
        Using(Dim g as Graphics = Me.CreateGraphics())
            g.DrawImage(Image, Location)
        EndUsing
    End Sub
    
    0 讨论(0)
  • 2020-12-04 04:29

    Drawing the image using a graphics object is the recommended procedure if you're going to use it as a cursor. But if you sometime want to use a PictureBox (for reasons like being able to quickly change image using it's Image property, etc), that is possible too.

    This code will draw a better "transparent" background, by drawing each control behind your PictureBox on it's background.

    How to use:

    1) Create a custom class.

    2) Put Inherits PictureBox below the Public Class ... line.

    3) Paste this code inside the class:

    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) Build your project.

    5) Select your class from the toolbox and add it to your form/usercontrol.

    0 讨论(0)
  • 2020-12-04 04:44

    This blog article inspired this SO answer. These were the basis for a more robust control with scaling, text, contentalignment etc.

    The following is a scaled back version (in VB) to primarily implement the appearance of true transparency. The core painting is nearly identical to the original SO post except to account for a border in the painting. A few control level features have also been retained.

    '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
    

    How to Use It

    1. Create a Class Library
    2. Replace the new class boilerplate code with the above
    3. Add references for the NameSpaces listed in the Imports statements
    4. Build the Library. A new TransPicBox should show in the toolbox.

    You can also just include the class code file in your project and rebuild to avoid a DLL dependency which includes just one thing. Results:

    • Four small PNGs under a larger one; all are on a Panel (rose BG)
    • The TopLeft and BottomRight images use a Transparent BG, the other 2 do not
    • They all have the border turned on to show the client area; the BL Clock uses a 3D border
    • The larger PNG uses ImageSizing.Scale to be about 150% larger

    The parent backcolor shows thru the TL and BR images as well as the overlapping larger one in front. The control will display BMP and JPGs normally and still show whats behind empty areas (if any) when the control's back color is Transparent.

    Notes:

    1. This is a fairly expensive Paint. Each time one of these needs to be painted, at least a portion of the parent and every control under it must be repainted as well.
    2. When moving a TransPicBox in the form designer, VS temporarily brings the control to the front, so the display is temporarily whacked
    3. Windows ambles on normally so things behind your special control are still hidden as far as it is concerned. The image below shows that the portions of a button partially behind a TransPicBox will not 'glow' when the mouse is over it. As far as Windows knows, that portion of it cant be seen so it is not repainted.

    0 讨论(0)
提交回复
热议问题