Search code examples
vb.netwinformsgraphicstransparencygdi+

Transparent image over two controls with different back colors


I am trying to place a transparent image over two adjacent controls that have different background colors.
I want the image to remain transparent, meaning the Image needs to show the backcolor of each control.

The controls are two Panels set to different background colors and the Image (PictureBox or otherwise) is placed between the two panel controls.

Public Class frmMain 
    Private Img1 As Image = Image.FromFile("C:\xxxx.png") 

    Private Sub frmMain_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint 
        e.Graphics.DrawImage(Img1, 5, 5) 
    End Sub 
End Class

enter image description here


Solution

  • Let's try this.

    • Create a new class in the Project, call it TPanel and paste in the custom Panel class you can find below, overwriting the existing definition.
    • Compile the Project then find the new TPanel control in the ToolBox and drop one instance inside a Form.
      On the Form, not inside one of the Colored Panels, otherwise it will become child of another control and it will be confined inside its bounds.
    • Add an event handler to the Paint event of the TPanel and insert this code inside the handler method:
    Private Sub TPanel1_Paint(sender As Object, e As PaintEventArgs) Handles TPanel1.Paint
        Dim canvas As Control = DirectCast(sender, Control)
        Dim rect As Rectangle = ScaleImageFrame(imgBasketBall, canvas.ClientRectangle)
    
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
        e.Graphics.CompositingMode = CompositingMode.SourceOver
        e.Graphics.PixelOffsetMode = PixelOffsetMode.Half
        e.Graphics.DrawImage(imgBasketBall, rect)
    End Sub
    
    Private Function ScaleImageFrame(sourceImage As Bitmap, destinationFrame As Rectangle) As Rectangle
        Dim rect As RectangleF = New RectangleF(0, 0, sourceImage.Width, sourceImage.Height)
        'Define the ratio between the Image Rectangle and the Container ClientRectangle
        Dim ratio As Single = CType(Math.Max(destinationFrame.Width, destinationFrame.Height) /
                                    Math.Max(rect.Width, rect.Height), Single)
        rect.Size = New SizeF(rect.Width * ratio, rect.Height * ratio)
        'Use Integer division to avoid negative values
        rect.Location = New Point((destinationFrame.Width - CInt(rect.Width)) \ 2,
                                  (destinationFrame.Height - CInt(rect.Height)) \ 2)
        Return Rectangle.Round(rect)
    End Function
    
    • In the Form, create an instance of a Bitmap object that will contain the Image; also set the Location of the Panel (TPanel)
      The Controls called panColored1 and panColored2 are supposed to be the names of the two existing Panels where the Image must be positioned. The sample code positions the Image in the middle of the 2 Panels, using TPanel1.Location( (...) )
    Private imgBasketBall As Bitmap = Nothing
    
    Public Sub New()
        InitializeComponent()
        imgBasketBall = DirectCast(Image.FromStream(New MemoryStream(File.ReadAllBytes("basketball.png"))), Bitmap)
        TPanel1.Size = New Size(120, 120)
        TPanel1.Location = New Point(panColored1.Left + (panColored1.Width - TPanel1.Width) \ 2,
                                     panColored1.Top + (panColored1.Height + panColored2.Height - TPanel1.Height) \ 2)
        TPanel1.BringToFront()
    End Sub
    

    Result:

    Transparent Panel Scaled Image

         Bitmap Size            Bitmap Size 
         (1245x1242)            (1178x2000)
    

    The TPanel (Transparent Panel) class:

    Imports System.ComponentModel
    
    <DesignerCategory("Code")>
    Public Class TPanel
        Inherits Panel
        Private Const WS_EX_TRANSPARENT As Integer = &H20
        Public Sub New()
            SetStyle(ControlStyles.AllPaintingInWmPaint Or
                     ControlStyles.UserPaint Or
                     ControlStyles.Opaque Or
                     ControlStyles.ResizeRedraw, True)
            SetStyle(ControlStyles.OptimizedDoubleBuffer, False)
        End Sub
    
        Protected Overrides Sub OnPaint(e As PaintEventArgs)
            e.Graphics.FillRectangle(Brushes.Transparent, Me.ClientRectangle)
            MyBase.OnPaint(e)
        End Sub
    
        Protected Overrides ReadOnly Property CreateParams() As CreateParams
            Get
                Dim parameters As CreateParams = MyBase.CreateParams
                parameters.ExStyle = parameters.ExStyle Or WS_EX_TRANSPARENT
                Return parameters
            End Get
        End Property
    End Class