0

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

Jimi
  • 29,621
  • 8
  • 43
  • 61
bikerider
  • 101
  • 8
  • Can you show how you're trying to draw/assign this Bitmap? + Do the Panels need to move at some point, or everything is *static*? Do you need to do something with this Bitmap? Does it move? Can it be clicked? Anything else that, maybe, can be used to better understand your context? – Jimi Jan 18 '19 at 19:13
  • Everything is static and will not move. I also do not need to do anything with the bitmap, just a static picture. I have tried overlaying another panel with an assigned background image and that cuts off half the image. Similar results with a picture box. As mentioned, drawing the image on the form without a container on the form paint event draws it behind everything. – bikerider Jan 18 '19 at 19:21
  • All right. Have you tried something to paint this Bitmap somewhere? Do you have some code? – Jimi Jan 18 '19 at 19:23
  • 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 – bikerider Jan 18 '19 at 19:28
  • Yes, you're painting on the Form surface. You'll have to paint on another control. Do you need to have two different Panels on the background of the image, or it could be just one Panel painted with two different colors? – Jimi Jan 18 '19 at 19:34
  • 1
    Possible duplicate of [No Transparency on Visual Basic PictureBox](https://stackoverflow.com/questions/32431300/no-transparency-on-visual-basic-picturebox) – Visual Vincent Jan 18 '19 at 19:36
  • It needs to be two different panels – bikerider Jan 18 '19 at 19:36
  • I tried the example in the posted link but getting an error when Inheriting PictureBox: Base class 'PictureBox specified for class 'TransparentPicBox' cannot be different from the base class 'Control' of one of its other partial types. – bikerider Jan 18 '19 at 20:27
  • Splitting the images in half seems like a cheap solution - sorry but no – bikerider Jan 18 '19 at 20:55
  • When you tried the code in my link, what kind of item did you add to the project? I realized that I wrote "component", but that was a terminology error from my end, as a `Class` would have been the right choice. :) – Visual Vincent Jan 19 '19 at 08:27

2 Answers2

2

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
Jimi
  • 29,621
  • 8
  • 43
  • 61
  • That worked great! I removed the relative positioning part and just set an x,y coordinate. Thanks! – bikerider Jan 18 '19 at 21:39
  • Sorry - one last question. How do I retain the original aspect ratio ration? Similar, to setting the Size Mode to Zoom? – bikerider Jan 18 '19 at 21:46
  • Yep, that can be an issue when the image is not perfectly square and its DPI is different from the Screen DPI. I'll update the code for that, it can be useful. BTW, take a look at this answer: [Image is not drawn at the correct spot](https://stackoverflow.com/a/51456467/7444103). The code is `C#`, but you just need the notes, if you don't *speak the language*. – Jimi Jan 18 '19 at 21:50
  • Worked great - this is very handy. Thank you! – bikerider Jan 18 '19 at 22:58
  • Lol, *speak the language*, it is written not "spoken". – preciousbetine Jan 19 '19 at 03:11
0

There is also something you can also try, It may not be professional but it works. Split the images into two halves. Draw the first half on one of the panels and the second half on the other panel. Be Sure to Import System.IO in Your project.

The code for the splitting goes like this:

Imports System.IO
...
Public Function SplitImage(ByVal imgpath As String) As Image()
    Dim img As Image = Image.FromFile(imgpath)
    Dim bmp As Bitmap = DirectCast(img, Bitmap)
    Dim i As Integer = bmp.Height / 2
    Dim image1 As Bitmap = New Bitmap(bmp.Width, i)
    Dim image2 As Bitmap = New Bitmap(bmp.Width, i)
    Dim yPos As Integer = 0
    For x As Integer = 0 To image1.Width - 1
        For y As Integer = 0 To image1.Height - 1
            image1.SetPixel(x, y, bmp.GetPixel(x, y))
            yPos = y
        Next
    Next
    yPos += 1
    Dim ycount As Integer = 0
    For x As Integer = 0 To image2.Width - 1
        For y As Integer = yPos To bmp.Height - 1
            If ycount = i Then
                ycount -= 1
            End If
            image2.SetPixel(x, ycount, bmp.GetPixel(x, y))
            ycount += 1
        Next
        ycount = 0
    Next
    Dim ms As MemoryStream = New MemoryStream
    Dim ms1 As MemoryStream = New MemoryStream
    image1.Save(ms, Imaging.ImageFormat.Png)
    image2.Save(ms1, Imaging.ImageFormat.Png)
    Dim returnedImage(2) As Image
    returnedImage(0) = image1
    returnedImage(1) = image2
    Return returnedImage
End Function

Create Two panels on your form (Panel1 and Panel2) and a Button(Button1). Place The two panels the way you want it, set the BackgroundImageLayout property of the panels to StretchImage. Then from your code you can call the function like this, i.e From the Button's click event:

Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim pic() As Image = SplitImage("C:\xxxx.png")
    Panel1.BackgroundImage = pic(0)
    Panel2.BackgroundImage = pic(1)
End Sub

For More Information about the Bitmap Class, Check out this link Bitmap Class

preciousbetine
  • 2,959
  • 3
  • 13
  • 29