Make a png image transparent - vb.net

I have a PictureBox added to my Panel1 with Panel1.Controls.Add(pb), and I have tried to make my .png picture transparent.
I have tried with Color.Transparent and with System.Drawing.Color.Transparent, but when I add the PictureBox to my Panel, I can not make it transparent.
And also I'm not able to bring to the front of the others images.
This is my code.
Private Function molduraint()
Dim pb As New PictureBox
pb.BringToFront()
pb.ImageLocation = OpenFileDialog1.FileName
pb.SizeMode = PictureBoxSizeMode.StretchImage
Panel1.Controls.Add(pb)
pb.Location = New Point(txtValueX.Text, txtValueY.Text)
If txtValueX.Text = 0 Or txtValueY.Text = 0 Then
pb.Location = New Point(300, 172)
End If
pb.Visible = True
pb.Size = New Size(TrackBar1.Value, TrackBar2.Value)
pb.Image = PictureBox1.Image
End Function

As you probably know, WinForms controls are not exactly designed to support true transparency (except Forms, those can be actually transparent).
Bitmaps, on the other hand, support transparency.
If you create a Bitmap object using an image format that supports an Alpha Channel, like a .png bitmap, you can draw that image preserving its transparency.
The first thing to do is to create an object that can be used to reference each Bitmap we want to draw, so we can keep track of them.
Since you want to be able to specify position and size of these objects, those are two of the properties that the object must have. I'm adding some more that can be helpful here.
Public Class BitmapObject
Public Property Name As String
Public Property Image As Bitmap
Public Property Position As Point
Public Property Size As Size
Public Property Order As Integer
End Class
The property Name will be the name of source file and Order will reference the z-order position of the Bitmap in relation to the other Bitmaps drawn inside a container.
All the Bitmaps will be grouped using a List of Bitmap objects, so we can summon them using the List Index or one of the properties.
Public MyBitmaps As List(Of BitmapObject) = New List(Of BitmapObject)
As for the drawing surface (canvas), we can use the Form itself, a PictureBox or a Panel (because they're - more or less - just surfaces). I prefer a Panel, it's lightweight, it can host other controls and can be moved around if needed.
If you want to draw on a control, you just need to subscribe its Paint() event and raise it calling the control's Invalidate() method.
Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
If MyBitmaps.Count > 0 Then
MyBitmaps.OrderBy(Function(item) item.Order).
Select(Function(item)
e.Graphics.DrawImage(item.Image, New Rectangle(item.Position, item.Size))
Return item
End Function).ToList()
End If
End Sub
To add a Bitmap to the List(Of BitmapObject), since you want to use an OpenFileDialog to let the user select a Bitmap, we assign this functionality to a Button and when the Bitmap is selected, a new BitmapObject is created and appended to the List.
Private Sub btnOpenFile_Click(sender As Object, e As EventArgs) Handles btnOpenFile.Click
Dim fd As OpenFileDialog = New OpenFileDialog()
fd.InitialDirectory = "[Images Path]"
Dim dr As DialogResult = fd.ShowDialog()
If dr = Windows.Forms.DialogResult.OK Then
Dim BitmapName As String = New FileInfo(fd.FileName).Name
Using tmpBitmap As Bitmap = New Bitmap(fd.FileName)
MyBitmaps.Add(New BitmapObject With {
.Image = New Bitmap(tmpBitmap),
.Position = New Point(Integer.Parse(TextBox1.Text), Integer.Parse(TextBox2.Text)),
.Size = New Size(tmpBitmap.Height, tmpBitmap.Width),
.Order = MyBitmaps.Count,
.Name = BitmapName})
ComboBox1.Items.Add(BitmapName)
ComboBox1.SelectedIndex = MyBitmaps.Count - 1
TrackBar1.Value = tmpBitmap.Height
TrackBar2.Value = tmpBitmap.Width
Panel1.Invalidate()
End Using
End If
End Sub
This is the result: (Full source code in PasteBin)

Related

Save zoomed image in a picturebox?

I'm a newbie in vb.net (windows app) programming. How can I save a zoomed image in a picturebox.
See, I have a panel and put a picturebox (set to zoom) inside and set the panel to autoscroll to accomodate the size of the image. I can zoom in and out of the image. I can save the picture as is using a memorystream and save it to the database (access). But the thing I don't know is how to save the current size of the image to the size and current position of the image relative to the size of the panel.
This is what my project looks like. See I can load an image and save the picture as is to the database.
What my project looks like
My question is, how to save the current location and size relative to the panel size of the image if I zoom it?
How to save this image and resize the picturebox to the size of the panel and the current location of the zoomed image and the image itself
I hope you understand my question (sorry if my english is bad, it is not my native language).
------edit------
UPDATE: I was able to save the zoomed image from the picturebox inside the panel. I used #dr.null and #jtxkopt suggestion and it works somehow. BUT the problem is, the output has also drawn the scrollbars on the panel.
Output:
This is my current output
This is my code:
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
SaveFileDialog1.Filter = "Image Files|*.jpg; *.png; *.bmp"
Panel3.HorizontalScroll.Visible = False
Panel3.VerticalScroll.Visible = False
If SaveFileDialog1.ShowDialog = DialogResult.OK Then
Dim imageRectangle = New Rectangle(Point.Empty, picUser.Image.Size)
Dim safeCropRectangle = Rectangle.Intersect(imageRectangle, Panel3.DisplayRectangle)
Dim bmp As Bitmap = New Bitmap(Panel3.Width, Panel3.Height, picUser.Image.PixelFormat)
Panel3.DrawToBitmap(bmp, safeCropRectangle)
bmp.Save(SaveFileDialog1.FileName)
End If
End Sub
What can I do to remove the scroll bars? I already used the panel.VerticalScroll.Visible = false before the lines of code but it doesn't work.
You can use the function DrawToBitmap to save modified image inside PictureBox.
Follow the below procedure.
Create a new bitmap with the same size and Pixel Format as the picturebox.
Call the DrawToBitmap function of the picturebox.
For more details, examine the following example program. Using DrawToBitmap, you can set the PictureBox.SizeMode property to whatever you want and save the modified image to a file.
PS: This example code is for giving the idea of how you can save the image of PictureBox in any SizeMode, not for providing a solution to the question with/without its complete details.
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks
Imports System.Windows.Forms
Namespace PictureboxZoomSave
Public Class MainForm
Inherits Form
Public Sub New()
InitializeComponent()
End Sub
Private openFileDialog As OpenFileDialog = New OpenFileDialog()
Private saveFileDialog As SaveFileDialog = New SaveFileDialog()
Private image As Image
Private Sub OpenImageButton_click(ByVal sender As Object, ByVal e As EventArgs)
openFileDialog.Filter = "Image Files|*.jpg; *.png"
If openFileDialog.ShowDialog() = DialogResult.OK Then
If image IsNot Nothing Then image.Dispose()
image = Image.FromFile(openFileDialog.FileName)
pictureBox1.Image = image
End If
End Sub
Private Sub SaveImageButton_click(ByVal sender As Object, ByVal e As EventArgs)
saveFileDialog.Filter = "Image Files|*.jpg; *.png"
If saveFileDialog.ShowDialog() = DialogResult.OK Then
Dim bitmap As Bitmap = New Bitmap(pictureBox1.Width, pictureBox1.Height, image.PixelFormat)
pictureBox1.DrawToBitmap(bitmap, New Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height))
bitmap.Save(saveFileDialog.FileName)
End If
End Sub
<STAThread>
Private Shared Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(New MainForm())
End Sub
Private components As System.ComponentModel.IContainer = Nothing
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso (components IsNot Nothing) Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
Private Sub InitializeComponent()
Me.pictureBox1 = New System.Windows.Forms.PictureBox()
Me.OpenImageButton = New System.Windows.Forms.Button()
Me.SaveImageButton = New System.Windows.Forms.Button()
(CType((Me.pictureBox1), System.ComponentModel.ISupportInitialize)).BeginInit()
Me.SuspendLayout()
Me.pictureBox1.Location = New System.Drawing.Point(12, 12)
Me.pictureBox1.Name = "pictureBox1"
Me.pictureBox1.Size = New System.Drawing.Size(200, 191)
Me.pictureBox1.TabIndex = 0
Me.pictureBox1.TabStop = False
Me.OpenImageButton.Location = New System.Drawing.Point(229, 12)
Me.OpenImageButton.Name = "button1"
Me.OpenImageButton.Size = New System.Drawing.Size(75, 23)
Me.OpenImageButton.TabIndex = 1
Me.OpenImageButton.Text = "Open"
Me.OpenImageButton.UseVisualStyleBackColor = True
AddHandler Me.OpenImageButton.Click, New System.EventHandler(AddressOf Me.OpenImageButton_click)
Me.SaveImageButton.Location = New System.Drawing.Point(229, 42)
Me.SaveImageButton.Name = "button2"
Me.SaveImageButton.Size = New System.Drawing.Size(75, 23)
Me.SaveImageButton.TabIndex = 2
Me.SaveImageButton.Text = "Save"
Me.SaveImageButton.UseVisualStyleBackColor = True
AddHandler Me.SaveImageButton.Click, New System.EventHandler(AddressOf Me.SaveImageButton_click)
Me.AutoScaleDimensions = New System.Drawing.SizeF(6F, 13F)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(800, 450)
Me.Controls.Add(Me.SaveImageButton)
Me.Controls.Add(Me.OpenImageButton)
Me.Controls.Add(Me.pictureBox1)
Me.Name = "PictureBoxZoomSave"
Me.Text = "PictureBoxZoomSave"
(CType((Me.pictureBox1), System.ComponentModel.ISupportInitialize)).EndInit()
Me.ResumeLayout(False)
End Sub
Private pictureBox1 As System.Windows.Forms.PictureBox
Private OpenImageButton As System.Windows.Forms.Button
Private SaveImageButton As System.Windows.Forms.Button
End Class
End Namespace
I understand that you need to crop the displayed region of a zoomed in image. If so, you can achieve that through many ways and techniques, DrawToBitmap is the easiest approach. Please note:
Using the PictureBox draw method and size doesn't output the required result because it's size changes when you apply the zoom/scale factor. In other words, the result is either a smaller or bigger image of the original one according to the zoom factor.
The output image size is the Panel client size (WYSIWYG).
Hence, you just need to do:
Dim imgRect = pnl.ClientRectangle
Dim bmp = New Bitmap(imgRect.Width, imgRect.Height, pbox.Image.PixelFormat)
pnl.DrawToBitmap(bmp, imgRect)
'Save it...
where pbox is the PictureBox and pnl is it's parent Panel.
That's it all if that is what you are after.

How to add infinite components when a button is clicked

I have a social media WinForm. I have a function that basically makes a new picture box when a button is clicked
Public Sub NewPost()
picture as new picturebox
picture.Width = 208
picture.Height = 264
picture.Image = Form2.PictureBox1.Image
picture.Location = New Point(258, 60)
End Sub
The thing is it only generates 1 new picture box because I have to make a new variable each time I want to add a picturebox, and eachtime I have to have a new name. I know my question Is a bit confusing but help would be nice thanks
If you want to trap events for your dynamic PictureBoxes, then you'll have to abandon the WithEvents model and move to using AddHandler.
Here's a quick example where the name of the PictureBox is displayed when it is clicked. Note that I am not setting a Location since they are being added to a FlowLayoutPanel which takes care of the placement for you:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
NewPost()
End Sub
Public Sub NewPost()
Dim picture As New PictureBox
picture.Width = 208
picture.Height = 264
picture.BorderStyle = BorderStyle.FixedSingle
' ...etc...
Dim index As Integer = FlowLayoutPanel1.Controls.Count + 1
picture.Name = "pb" & index
AddHandler picture.Click, AddressOf picture_Click
FlowLayoutPanel1.Controls.Add(picture)
End Sub
Private Sub picture_Click(sender As Object, e As EventArgs)
Dim pb As PictureBox = DirectCast(sender, PictureBox)
Debug.Print(pb.Name)
End Sub
End Class
because I have to make a new variable each time
Not necessarily. You just want to keep a reference to the object. That reference doesn't need to be its own variable, it can just as easily be an element in a list. For example, suppose on your form you have a list of PictureBox objects as a class-level member:
Dim pictureBoxes As New List(Of PictureBox)()
Then in your method you can just add to that list:
Public Sub NewPost()
Dim pictureBox As New PictureBox
pictureBox.Width = 208
pictureBox.Height = 264
pictureBox.Image = Form2.PictureBox1.Image
pictureBox.Location = New Point(258, 60)
Me.pictureBoxes.Add(pictureBox)
End Sub
In this case the pictureBox variable is local to the NewPost method and gets re-created each time. But pictureBoxes is a class-level member and keeps track of the growing list of PictureBox objects that you're creating.
You can use a for while loop to create n number of objects
You can use the existing ControlCollection
Public Function NewPost() As String
Dim picture As New PictureBox
'your code
picture.Name = "Pb" & Form2.Controls.OfType(Of PictureBox).Count
Form2.Controls.Add(picture)
Return picture.Name
End Function
then you can retrive it
DirectCast(Form2.Controls(NewPost), PictureBox).Image = Form2.PictureBox1.Image
'OR
DirectCast(Form2.Controls("Pb12"), PictureBox).Image = Form2.PictureBox1.Image

I draw image to form, but it is limited to top left corner of form

I have created a form and imported two square images saved as PNG files in resources. when I run the code below the black box which is drawn will only go about 200 pixels in the x coordinate and 150 pixels in the Y coordinate from where the image is drawn, after that the background remains white, and it seems I am unable to draw anything and anything I do draw stops around this point.
I have tried redrawing the image in a completely different location on the screen and It will not be visible if it is not within the region to the top left of the form, I have also tried drawing other images, but they also cease to exist when not in the top left of my form.
What I want is for the black box/other images to be drawn across the whole form, and not just in the top left corner, which something is preventing me from doing.
Public Class Form1
Dim gameGraphics As System.Drawing.Graphics = Me.CreateGraphics
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Draws black square which I have saved as resource
gameGraphics.DrawImage(My.Resources.black_Background, 0, 80, 1600, 600)
'Draws green square which I have saved as resource
gameGraphics.DrawImage(My.Resources.greenSquare, 2, 82, 40, 40)
End Sub
'makes the form fullscreen
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.FormBorderStyle = FormBorderStyle.None
Me.WindowState = FormWindowState.Maximized
End Sub
'closes form if quitbutton is clicked
Private Sub QuitButton_Click(sender As Object, e As EventArgs) Handles QuitButton.Click
Me.Close()
End Sub
End Class
Thanks for your time!
The Graphics Object cannot be stored. It's Erased/Updated constantly. You'll end up with an invalid one. It's really useless and, you could say, a mistake.
You can use a Graphics Object created with Control.CreateGraphics(), but you have to remember that it's not persistent; it will be erased when the Control you have painted it on needs to re-Paint() itself (e.g. you drag something over it, if it's a Form, when it's minimized etc.).
Those Properties, Me.FormBorderStyle = FormBorderStyle.None and Me.WindowState = FormWindowState.Maximized are better set in the designer. There's no reason to set them on a Form.Load() event. Their state is not even subject to a condition. In general, leave the Load event of a Form as lightweight as possible and avoid setting properties that can cause cascading events.
An example:
Define an object to store your images:
(The DrawBitmaps flag is used to let your Form know when to draw those Bitmaps).
Public Class MyBitmap
Public Property Image As Bitmap
Public Property Position As Point
Public Property Size As Size
End Class
Public MyBitmaps As List(Of MyBitmap)
Public DrawBitmaps As Boolean = False
Somewhere (even in Form.Load()), fill the list with you bitmaps:
(Here, the bitmap size is set to original size, but you can set it to whatever dimension you see fit).
MyBitmaps = New List(Of MyBitmap)
MyBitmaps.Add(New MyBitmap With {.Image = My.Resources.black_Background,
.Position = New Point(0, 80),
.Size = New Size(My.Resources.black_Background.Width,
My.Resources.black_Background.Height)})
MyBitmaps.Add(New MyBitmap With {.Image = My.Resources.greenSquare,
.Position = New Point(2, 82),
.Size = New Size(My.Resources.greenSquare.Width,
My.Resources.greenSquare.Height)})
The Paint() event e.Graphics of the Form performs all the painting:
(Note that it will not paint its surface unless the DrawBitmaps flag is set to True => It will not paint those Bitmaps when it's loading/showing. The other condition is a basic fail-safe.
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles MyBase.Paint
If DrawBitmaps = True AndAlso MyBitmaps.Count > 0 Then
For Each _Item As MyBitmap In MyBitmaps
e.Graphics.DrawImage(_Item.Image, New Rectangle(_Item.Position, _Item.Size))
Next
End If
End Sub
When Button1 is clicked, the Form will draw your list of Bitmaps:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
DrawBitmaps = True
Me.Invalidate()
End Sub
Somewhere in your code, add a new Bitmap and tell the Form to Invalidate only a region of the size of this new Bitmap:
MyBitmaps.Add(New MyBitmap With {.Image = My.Resources.[AnotherBitmap],
.Position = New Point(50, 50),
.Size = New Size(200, 200)})
Me.Invalidate(New Rectangle(MyBitmaps.Last().Position, MyBitmaps.Last().Size))
Remove a Bitmap from the list and repaint:
MyBitmaps.RemoveAt(0)
Me.Invalidate()

ListView displays incorrect images for each item

I have a listview that lists all items from a directory like so:
But as you can see in this image:
They are not in order, because one image text has been skipped entirely, however the image itself is still in order.
But, when it gets further down the list, starting from about half way they start becoming completely mixed up like in this example:
Image 3
When clicking an image it shows the preview of the correct image on the right side.
image 4
This is the code I'm using to load in all the images:
Dim imgList As New ImageList
Dim imgSize As New Size
Dim count As Integer = 0
Dim imgFilename As String
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Dim imlTemp As New ImageList
Dim dirFiles() As String = IO.Directory.GetFiles(My.Settings.GalleryLocation)
'Dim item As New ListViewItem
For Each dirFile As String In dirFiles
imgFileName = IO.Path.GetFileName(dirFile)
Dim img As New System.Drawing.Bitmap(dirFile)
Dim imgImage As Image = Image.FromFile(dirFile)
'Dim imgHeight As Integer
'imgHeight = imgImage.Height
imgSize.Width = 120
imgSize.Height = 174
Threading.Thread.Sleep(10)
BackgroundWorker1.ReportProgress(100 / ((dirFiles.Count + 1) - count), img)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ListView1.SmallImageList = imgList
ListView1.LargeImageList = imgList
imgList.ImageSize = imgSize
imgList.ColorDepth = ColorDepth.Depth32Bit
ListView1.Items.Add(imgFilename, count)
imgList.Images.Add(e.UserState.Clone)
count += 1
'ListView1.EnsureVisible(ListView1.Items.Count - 1)
End Sub
Before I added in the backgroundworker it had tremendous loading times for a large amount of images, so I thought I'd implement the backgroundworker to allow async work to be done. However, something is going completely wrong at the start of the task and multiple times part way through the list, where it completely messes up as shown in Image 3.
Does anybody have any idea what is going wrong, or any alternative solutions to what I'm aiming to do?
Since ReportProgress() is not blocking (as far as I know), there is a chance that the files are iterated though faster than the UI can update.
To keep this synchronized you should make a custom class holding everything you want to update and pass that to the ReportProgress() method (even making some steps automated).
For example:
Public Class GalleryImage
Public Property FullPath As String
Public Property FileName As String
Public Property [Image] As Image
Public Sub New(ByVal File As String)
Me.Image = Image.FromFile(File) 'Get the image.
Me.FullPath = File 'Save the full path of the image.
Me.FileName = Path.GetFileName(File) 'Get the file name.
End Sub
End Class
(Also, if you want the file name but not the extension you can use Path.GetFileNameWithoutExtension())
Now to your code. First of all the counter variable isn't really necessary as it can be replaced by either ListView1.Items.Count or imgList.Images.Count.
Secondly, you shouldn't constantly keep setting the imgSize variable nor the ListView's Small-/LargeImageList properties. Doing that is completely unnecessary and will slow things down. For the ListView just set the image list once, and for the imgSize variable you can do like this:
Dim ReadOnly imgSize As New Size(120, 174)
Making the variable ReadOnly does what it sounds like; you can read from it, but not modify it.
Now to fix these other things we'll start in the BackgroundWorker's For Each loop:
For Each dirFile As String In dirFiles
Threading.Thread.Sleep(10) 'You don't need this Sleep if you don't want to.
'Report the progress, declare an in-line version of our class and send it with.
BackgroundWorker1.ReportProgress(100 / ((dirFiles.Count + 1) - ListView1.Items.Count), New GalleryImage(dirFile)) 'A new GalleryImage is created. The property setting is handled by the class itself.
Next
As you see we have now narrowed the code down rather much.
Now we are going to handle the ReportProgess() event:
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Dim img As GalleryImage = e.UserState 'Retrieve our class, no need to clone it.
imgList.Images.Add(img.Image) 'Add the image first (I don't know for sure, but if you add the image after, wouldn't it require to redraw the ListView an extra time?).
ListView1.Items.Add(img.FileName, imgList.Images.Count - 1) 'Add the new item with the values from our class.
End Sub
And lastly, the initialization and initial setting of stuff should be done in for example the Form Load event:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'In here we do all the standard settings and initializations.
ListView1.SmallImageList = imgList
ListView1.LargeImageList = imgList
imgList.ImageSize = imgSize
imgList.ColorDepth = ColorDepth.Depth32Bit
End Sub
And by that I think I should have covered it all.
Hope this helps!

Refining a custom listbox control

I have made a sort of custom list box and have included the ability to add an image to each item.
I want to be able to change these images one by one at will and am not too sure on how to go about it. At the moment you can only select which image you would like in every item.
Public Class CustomListBox
Public label As Label
Public pic As PictureBox
Public panel As Panel
Public itemID As String
Public itemCollection As New Collection
Public bgColor As Color
Public txtEnterColor As Color = Color.FromArgb(80, 80, 80)
Public txtColor As Color = Color.FromArgb(150, 150, 150)
Public bgEntercolor As Color = Color.FromArgb(230, 230, 230)
Public x, y, paddingInt As Integer
Public itemHeight As Integer = 40
Public image As Image = My.Resources.FavNone
Public Event Item_Clicked()
Private Property ItemBackColor As Color
Get
Return BackColor
End Get
Set(ByVal value As Color)
bgColor = value
End Set
End Property
Private Property ItemPadding As Padding
Get
Return Padding
End Get
Set(ByVal value As Padding)
Padding = value
End Set
End Property
Public Property HoverBackColor As Color
Get
Return bgEntercolor
End Get
Set(ByVal value As Color)
bgEntercolor = value
End Set
End Property
Public Property ItemImage As Image
Get
Return image
End Get
Set(ByVal value As Image)
image = value
End Set
End Property
Public Property HoverTextColor As Color
Get
Return txtEnterColor
End Get
Set(ByVal value As Color)
txtEnterColor = value
End Set
End Property
Public Property TextColor As Color
Get
Return txtColor
End Get
Set(ByVal value As Color)
txtColor = value
End Set
End Property
Public Property TrueItemHeight As Integer
Get
Return itemHeight
End Get
Set(ByVal value As Integer)
itemHeight = value
End Set
End Property
Public Sub UpdateItems()
For Each item As String In itemCollection
label = New Label
pic = New PictureBox
panel = New Panel
With pic
.Width = itemHeight
.Height = itemHeight
.SizeMode = PictureBoxSizeMode.Zoom
.Image = image
End With
With label
.BackColor = (bgColor)
.ForeColor = (txtColor)
.Width = Me.Width - itemHeight
.Height = itemHeight
.Tag = item
.Height = itemHeight
.Padding = ItemPadding
.Text = item
.Left = itemHeight
.TextAlign = ContentAlignment.MiddleLeft
AddHandler label.MouseEnter, AddressOf Item_Enter
AddHandler label.MouseLeave, AddressOf Item_Leave
AddHandler label.MouseUp, AddressOf Item_Mousedown
End With
With panel
.Location = New Point(x, y)
.Width = Me.Width
.Height = itemHeight
.Controls.Add(pic)
.Controls.Add(label)
y += .Height + paddingInt
End With
Me.Controls.Add(panel)
Next
End Sub
Private Sub Item_Enter(ByVal sender As Label, ByVal e As EventArgs)
sender.BackColor = (bgEnterColor)
sender.ForeColor = (txtEnterColor)
itemID = sender.Tag
End Sub
Private Sub Item_Leave(ByVal sender As Label, ByVal e As EventArgs)
sender.BackColor = (bgColor)
sender.ForeColor = (txtColor)
End Sub
Private Sub Item_Mousedown(ByVal sender As Label, ByVal e As MouseEventArgs)
Select Case e.button
Case Windows.Forms.MouseButtons.Left
RaiseEvent Item_Clicked()
End Select
End Sub
End Class
I know I'll need to add something into the click event of the items.Possible set a variable with the index number of the label, maybe...?
Also, how do I get auto-suggestions when typing the code. For example, I want to be able to type CustomListBox1.itemCollection.add("Text", imageSrc)...etc I just have no idea what to type into Google apart from looking through loads of custom controls until I find one that includes this.
EDIT
I have looked into this custom Listbox.
I tried to add a MouseMove event to each item so thought it would be as easy as placing this:
Private Sub HoverItem(ByVal item As ColorListboxItem, ByVal e As MouseEventArgs)
msgbox(1)
End Sub
...in the "Methods" region and then
AddHandler .mousemove, AddressOf HoverItem
to the "OnDrawItem" sub. Unfortunately for me, it obviously isn't that easy as no msgbox shows.
Could anybody with experience on this control give a bit of insight as to how it works. And maybe an example of the MouseMove event so then I'll get an idea on how to add in more events (Mouseleave, DblClick...etc)
What #JoshMason said is correct for the most part, but if you insist on doing that, then you need not only an indexed collection/array for your items but also a linked associated collection/array for the corresponding images.
So that way the index of the item/image is accessible to you (properly exposed of course) so that you can assign an image to it like you would assign a text to the item(index) and you need to make sure your code accounts for removing an item also removes the corresponding image, but if you link them properly, that should happen automatically.
EDIT: For inspiration, give this a quick look.
That looks familiar - I have something very similar to house and track a bunch of thumbnails. A few things. The way it is written it is more of a ListBox helper than a custom control. There is nothing wrong with that, but if you want it to show up in the toolbox and be more reusable, consider reworking it:
Public Class CustomListBox
Inherits Panel ' or maybe Component, depending....
The way yours is written, you are trying to emulate ListBox functionality by maintaining a collection of labels and picboxes and panels. If you start to think about each panel+picbox+textbox as its OWN integral thing (control), you can internalize some of the functionality at that level (Event processing for instance) and leave the ListBox helper mainly to manage the interactions with the user or app (or go away). I dont think auto-suggest is going to work until it is an actual control or component.
Private WithEvents mLbl As TextBox ' just recently decided to allow text edits
Private WithEvents mPic As PictureBox
Public Sub New(ByVal uniqueName As String)
mLbl = New TextBox
mPic = New PictureBox
Name = uniqueName
.... set required lbl/txt properties
MyBase.Controls.Add(mLbl) ' we inherit from Panel
.... set pic controls
MyBase.Controls.Add(mPic)
...
...
' no need for AddHandler, each Item comes with its own built in event
Private Sub mPic_DClick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mPic.DoubleClick, mLbl.DoubleClick
IsSelected = Not _IsSelected
End Sub
After it is created with default or basic props, the class that is creating it sets the unique ones like Text, Image and location on the scrolling panel before adding it to the scrollpanel:
frmMain.pnlImgList.Controls.Add(newImgItem)
IsSelected (above) is a property, that when it changes from False to True, I raise a new event ItemSelected to notify the app/panel housing each "ImgItem" Control. The app need not know whether it was the textbox or pic clicked because ImgItem will handle that (like edit Text). In your case, this could be changing the color when selected/focused etc. (Breaking it down into 2 pieces would eventually let you get rid of that big procedure to create all new items).
What I dont have is any internal collection of these. They are added to a panel on the form and that panel's controls collection serves that purpose. When a new one of these things is added to the form, it DOES have to be hooked up to events using AddHandler to process events like ItemSelected (there is a ControlAdded/ControlRemoved event that makes a nice spot for hooking/unhooking these to the Selected event handler!)
You could do something similar using a scrolling panel of items which looks and acts like a Listbox.
Before you can do things like change an image, you need a unique identifier so you know WHICH picture to change. It looks like you use the text as the name. Depending on how you use it, that might work but if the user can edit the text, they could create a duplicate; and if the name changes you run the risk of loosing track of things. So, consider tagging them with your own unique name that isnt exposed to the user:
' this would be passed to Sub New when creating a new Item
newName = System.Guid.NewGuid.ToString()
The unique ID (Name) is part of the events args in the ItemSelected event, making it easy to find each control on the form:
ThisCtl = pnlItems.Controls(sender.Name)
To change an image, just expose it at the "item" level:
Friend Property Pic() As Bitmap
Get
' I dont recall why this is this way, maybe an artifact
' from trying different things.
Return CType(mPic.BackgroundImage, Bitmap)
End Get
The form or your helper can then change the image:
ThisCtl = pnlItems.Controls(sender.Name)
ThisCtl.Pic = newImage
Or frmName.pnlItems(sender.Name).Pic = newImage
Re your edit: Some of what you want to do with the mouse (Change colors) might be able to be done on the cheap by changing BackColor in mouse events. Some things though might be better handled by making it a proper component so that you can Shadow and OVerride mouse and paint procedures as needed. If you keep the items in an actual ListBOx, you almost certainly are going to have to hook into the DrawItem paint events. Worry about that after you decide whether or not to convert it to a component.
HTH