# VBForums CodeBank > CodeBank - Visual Basic .NET >  ListBox with custom items (colors, images, text alignment)

## NickThissen

Hey,

Here is a very simple owner-drawn ListBox control that allows you to specify a Color and Image for each item. It uses a very simple method to only allow items of a specific type (and not all Objects as usual) which is easily extended. 



I know you could just use a ListView for this, but I came across this method when creating a different control (a 'large item' listbox that looks like the Downloads window in FireFox) and decided to share it, because it is conceptually easy and allows you to extend the items much more. For example, there's nothing stopping you to give each item it's own Font or something.

As a 'bonus' there is also a TextAlign property which allows you to align the text of all items to whichever side you want (MiddleLeft in the image).



How it works:

The *ColorListBox* class inherits ListBox, and overloads the *Items* property. Instead of returning *MyBase.Items*, it returns its own *ColorListBoxItemCollection*. 
The original base items are then returned by a (private) property *baseItems*.

vb.net Code:
Private _Items As ColorListBoxItemCollection    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _    Public Overloads ReadOnly Property Items() As ColorListBoxItemCollection        Get            Return _Items        End Get    End Property     'The original items that the user will never see.    Private ReadOnly Property baseItems() As ObjectCollection        Get            Return MyBase.Items        End Get    End Property

The ColorListBoxItemCollection inherits System.Collections.ObjectModel.Collection(Of ColorListBoxItem) and overrides the Set/Remove/InsertItem and ClearItems methods. In those methods, it adds the item to it's collection but _also_ adds the item to the baseItems property of the owner ColorListBox. This is necessary because the ColorListBox won't draw any items in your own custom collection; only those in the MyBase.Items collection.

vb.net Code:
Protected Overrides Sub ClearItems()            MyBase.ClearItems()            _listBox.baseItems.Clear()        End Sub         Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As ColorListBoxItem)            MyBase.InsertItem(index, item)            _listBox.baseItems.Insert(index, item)        End Sub         Protected Overrides Sub RemoveItem(ByVal index As Integer)            MyBase.RemoveItem(index)            _listBox.baseItems.RemoveAt(index)        End Sub         Protected Overrides Sub SetItem(ByVal index As Integer, ByVal item As ColorListBoxItem)            MyBase.SetItem(index, item)            _listBox.baseItems(index) = item        End Sub

Finally, the ColorListBox is owner drawn, so it overrides the OnDrawItem method. The listbox actually _wants_ to draw the original items, but I simply force it to use the items from my own collection (by using their index), and draw them with the correct color and possibly with an image.




As I said, this is the 'base' for a different ListBox control which I'll hopefully also complete shortly.

I am not 100&#37; sure whether this method will always work (I am afraid the baseItems and Items might become 'de-synchronised' for some reason for example), but I hope it will work fine.


Enjoy!

----------


## kayleigh

I'm having real trouble with this.  :Frown:  

i added the control not a problem.

i added your code above and got errors. do you have that image as an example still that i could look at?

----------


## NickThissen

You don't need to add any code, the code is all in the vb file. If you added the control without problems then all you need to do is build the project and it will be in your toolbox like any other control.

----------


## kayleigh

edit resolved ta  :Smilie:

----------


## TDQWERTY

I'd like to detect clicked regions in each and every item from the ColorListBox, but i dunno what has to be done in this class of yours to accomplish that. Wouldn't you have any idea would you?
Maybe creating an other method "ItemClickedSpot"? But then, how?

Thanks

Edit:
Here's how i managed to do it...for this thread here: http://www.vbforums.com/showthread.php?t=615255



vb.net Code:
Imports System.ComponentModel Public Class ColorListBox    Inherits ListBox     Public Enum HotSpots As Integer        ITEM_IMAGE = 0        ICON1 = 1        ICON2 = 2        ICON3 = 3        DoubleClick = -1    End Enum     Public Event ClickedHotSpot(ByVal Index As Integer, ByVal Spot As HotSpots, ByVal Tag As String)     Public Shadows Sub ItemClickedSpot(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown         If e.Button = MouseButtons.Left Then            If e.Clicks = 1 Then                Select Case (e.Y - (Me.IndexFromPoint(e.X, e.Y) * Me.ItemHeight))                    Case 22 To 63                        Select Case e.X                            Case 94 To 126                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ICON1, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)                            Case 234 To 266                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ICON2, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)                            Case 374 To 406                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ICON3, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)                        End Select                End Select                 Select Case (e.Y - (Me.IndexFromPoint(e.X, e.Y) * Me.ItemHeight))                    Case 10 To 58                        Select Case e.X                            Case 14 To 56                                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.ITEM_IMAGE, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)                        End Select                End Select             End If            If e.Clicks = 2 Then                RaiseEvent ClickedHotSpot(Me.IndexFromPoint(e.X, e.Y), HotSpots.DoubleClick, Me.Items.Item(Me.IndexFromPoint(e.X, e.Y)).Tag)            End If        End If    End Sub'...

Implementing

vb.net Code:
Private Sub ColorListBox1_ClickedHotSpot(ByVal Index As Integer, ByVal Spot As ColorListBox.HotSpots, ByVal Tag As String) Handles ColorListBox1.ClickedHotSpot        MsgBox("Item number: " & Index + 0 & " - button: " & Spot.ToString & " TAG: " & Tag)    End Sub

Suggestions will be very welcome

----------


## NickThissen

Ok, a couple of suggestions:

1. Instead of handling the MouseDown event of your own class, you should generally override the OnMouseDown method instead. It has the same end effect, but there are a few reasons why that approach is better (I can't remember any though...)

2. You seem to be hardcoding the 'hotspots' by their X and Y values. What if you decide to shift an icon 2 pixels? Then you'd need to change your code... Not very desirable I think!

What you can do instead is make use of the fact that my ListBox is using custom items; the ColorListBoxItem class. As of now, it has only three properties (Text, Color and Image), but you can add to that of course. 

Why not add a few Rectangle properties that represent your hotspots. You would have a property for every hotspot (Icon1, Icon2, Icon3, ItemImage, etc), representing the bounds of that hotspot.
Then, in your MouseDown event (or OnMouseDown override, see point 1), you can retrieve the item that the mouse is clicked on, and then check if the click location is inside any of the hotspots:

vb.net Code:
For i As Integer = 0 To Me.Items.Count - 1   If Me.GetItemRectangle(i).Contains(e.Location) Then      ' This is your item      Dim item As ColorListBoxItem = Me.Items(i)       If item.Icon1.Contains(e.Location) Then         RaiseEvent ClickedHotSpot(... Icon1)      ElseIf item.Icon2.Contains(e.Location) Then         RaiseEvent ClickedHotSpot(... Icon2)      'etc      End If       ' We've already found our item so stop looking      Exit For   End IfNext

Now, if you want to shift/enlarge/whatever Icon2 (for example), you just change the Icon2 Rectangle property of the item, and your code still works.

You can do the same in the OnMouseMove method and OnMouseUp method to simulate a button; change the border of the icon when the mouse is moving over the item, change it another way when the mouse is down, and change it back when the mouse is up.
How you're going to implement that depends on how you're drawing the icons. I suppose you have edited the OnDrawItem method so that it draws the icons too? By the way, if you have, you can make use of the Icon1, Icon2, etc properties too. Just draw the icon using those Rectangles.

----------


## TDQWERTY

Good point...the square around the icons!
Thanks

(important, ItemHeight must be 70)
Here's how your class looks like in my implementation...i'm still twicking it so it's supposed to have a few bugs..
Here's how it's looking at the moment

----------


## iactulip

nice

----------


## Xoslize

Hey Nick.

Mind explaining me as a mini-guide how to make my Icon + Text ListBox?
Thanks.

----------


## NickThissen

You have the source code, so why not take a look? If you have a specific question I'd be happy to answer it, but try to see it in the source code first, it's not very hard at all.

----------


## coolcurrent4u

i get error when i try to buld my solution with the control added





> Error 1 'down' is not a member of 'Resources'. ColorListBox2.vb 128 34  
> Error 2 'up' is not a member of 'Resources'. ColorListBox2.vb 134 34  
> Error 3 Type 'OwnerDrawnListBox' is not defined. ColorListBox2.vb 446 14  
> Error 4 'CreateGraphics' is not a member of 'color_listbox.FontListBox'. ColorListBox2.vb 456 29  
> Error 5 'ItemHeight' is not a member of 'color_listbox.FontListBox'. ColorListBox2.vb 457 9  
> Error 6 'Font' is not a member of 'color_listbox.FontListBox'. ColorListBox2.vb 457 50  
> Error 7 'Items' is not a member of 'color_listbox.FontListBox'. ColorListBox2.vb 464 25  
> Error 8 'SelectedIndex' is not a member of 'color_listbox.FontListBox'. ColorListBox2.vb 464 34  
> Error 9 sub 'OnPaint' cannot be declared 'Overrides' because it does not override a sub in a base class. ColorListBox2.vb 484 29  
> ...

----------


## moti barski

i have no idea how to use this
walkthrough please :
1 how to add the control ?
2 how to customize the control ?
3 where do i put the code of post 1, is it neccesary ?

----------


## NickThissen

Just add the file to your project and build the solution, then it should come up in the toolbox as usual. Drag it to your form and add items via the Items property.
You don't need the code of post 1, that is already in the control, I just posted it to clarify how it works in case anyone was interested.

----------


## coolcurrent4u

i added the control just as Nick described, but the control is not the one in post #1, but post #7.

----------


## moti barski

> Just add the file to your project and build the solution, then it should come up in the toolbox as usual. Drag it to your form and add items via the Items property.
> You don't need the code of post 1, that is already in the control, I just posted it to clarify how it works in case anyone was interested.


i downloaded : colorlistbox.vb
how do i add it from vb.net ?
you also said "then build the solution" what is that supposed to mean ? how ?
project (on the menu) then what ?
walkthrough please

----------


## NickThissen

See your Solution Explorer toolwindow. Rightclick the project you want it to be used in and select "Add - Add Existing File". Choose the .vb file and add it. Then go to the Build menu and select Build solution (or rightclick the project and select Build from there). If the build succeeds (you can see this in the output window) when there are no errors then the control should appear at the top of the toolbox.

----------


## moti barski

it worked

----------


## coolcurrent4u

> Good point...the square around the icons!
> Thanks
> 
> (important, ItemHeight must be 70)
> Here's how your class looks like in my implementation...i'm still twicking it so it's supposed to have a few bugs..
> Here's how it's looking at the moment


hello, there is a bug in your control. when you click and item then click another, the previous doesn't return to the normal state.

have you corrected this, can you tell me how?

----------


## moeb1us

nice work

you thought about doing a listview version of this?

----------


## formlesstree4

> nice work
> 
> you thought about doing a listview version of this?


I recommend you look at the ObjectListView.

----------


## dummies22

i'm sorry but how do i add image into the item list. i can't do the below can you help me please. thanks

Dim img As Image
img = Image.FromFile("c:\temp\thumbnails\" & filename)
ColorListBox1.Items.Add(img)

----------


## dummies22

nevermind got it.

----------


## rojaldearintok

how do you check for duplicate items, I'm adding folder path, i want to check for duplicate, I'm using this code, but there is a problem, Its say *"Value of type 'String' cannot be converted to 'ColorListBoxItem'."*, it usually work on the normal listbox but this one is not working

If fFolder.ShowDialog = DialogResult.OK Then
     If Not lstFolder.Items.Contains(fFolder.SelectedPath) Then
           lstFolder.Items.Add(fFolder.SelectedPath, Color.Black, My.Resources.FolderHorizontal)
      End If
End If

----------


## formlesstree4

> how do you check for duplicate items, I'm adding folder path, i want to check for duplicate, I'm using this code, but there is a problem, Its say *"Value of type 'String' cannot be converted to 'ColorListBoxItem'."*, it usually work on the normal listbox but this one is not working
> 
> If fFolder.ShowDialog = DialogResult.OK Then
>      If Not lstFolder.Items.Contains(fFolder.SelectedPath) Then
>            lstFolder.Items.Add(fFolder.SelectedPath, Color.Black, My.Resources.FolderHorizontal)
>       End If
> End If


If you look at the ColorListBoxItem class, you will see there is a Text property that you can use.

----------


## rojaldearintok

can you show me how, I try DirectCast as a String, not work...

----------


## formlesstree4

> can you show me how, I try DirectCast as a String, not work...


I'm want to say no because the source code is literally in front of you.



```
ColorListBoxItem1.Text
```

----------


## rojaldearintok

okay thanks, i will try it...

----------


## ebs111

hi 

i just used your Colorlistbox and its great .

but i have one problem the mouse cursor is fixed on Wait and i cant change it not with the propery and not in code.

please help.

thanks.

----------


## yvne

I tried but it's not working
need help sir, i really need this colorlistbox to my program

----------


## JuggaloBrotha

> I tried but it's not working
> need help sir, i really need this colorlistbox to my program


What error messages are you getting?
What where the steps you've tried?

----------


## yvne

> What error messages are you getting?
> What where the steps you've tried?

----------


## JuggaloBrotha

> 


Not sure why it's erroring like that in the ToolBox, when I have this control in my project's it shows at the very top (once I've compiled the project) and it has a purple gear icon. Maybe you could try removing it from your toolbox, recompile your project and see if it shows up at the very top.

Regardless of the toolbox issue, when you manually add it to a form does it still function normally?

----------


## Simbiose

Hi there.
Sorry for necroing this thread, but I have a question about this awesome custom control.

I'm using this custom control to list visited websites that were bookmarked by the user. I've coded a procedure, which allows the user to see the URL corresponding to the bookmark, which is being hovered by the mouse.

Everything works perfectly, but the control flickers like there's no tomorrow ...
Is there a way to remove or at least considerably reduce the flickering? I've tried the *doublebuffered* property on the form itself, but that doesn't work.

----------


## Edgemeal

> Everything works perfectly, but the control flickers like there's no tomorrow ...
> Is there a way to remove or at least considerably reduce the flickering? I've tried the *doublebuffered* property on the form itself, but that doesn't work.


Instead of setting DoubleBuffered on the Form try setting DoubleBuffered in the control(s) that flicker, like in the constructor of the ColorListBox class...



```
Public Class ColorListBox
    Inherits ListBox
#Region " Constructor "
    Public Sub New()
        Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
        Me.DoubleBuffered = True
' .....
```

----------


## Simbiose

> Instead of setting DoubleBuffered on the Form try setting DoubleBuffered in the control(s) that flicker, like in the constructor of the ColorListBox class...
> 
> 
> 
> ```
> Public Class ColorListBox
>     Inherits ListBox
> #Region " Constructor "
>     Public Sub New()
> ...


Lol! Major D'OH. Why is my brain thinking this way?  :LOL: 
Well, the double buffer works, but only if the listbox items don't have a really long bookmark and/or really long URL, which rarely happens lol...

I probably need to do a bit more of tweaking of the drawing event I guess.

----------


## Simbiose

FML! You can't set the listbox item image on run-time? You can only do it be using the *Properties* Window of the listbox?
The data is loaded into the listbox from a fed datatable, so I can only define the images for each item on run-time ............
Any work around for this?

*Edit* *Hard facekeyboard* You CAN set the listbox item image on run-time... what was happening to me, was that I have a textbox that has a textchanged event that handles the way that the adapter feeds the datatable and consequently the datatable would be in charge of adding items to the listbox.
So whenever I loaded the form, the textbox would add items to the listbox, instead of the load event of the form (which was where I was handling the item image adding).
All I had to do was add the images on the textbox event*

----------


## mr_sepehry

hi
how to change image size in item ListBox with custom items
please help me

----------


## Shaggy Hiker

I removed the link to your email address. Posting that in a forum post is not a good idea, as it will be sucked up by a bot and you'll end up with lots of spam.

I'm not clear whether your question is regarding the custom listbox in this thread or in some other design. If it isn't based on the techniques discussed in this thread, then you should create your own thread over in the .NET forum for the question.

----------

