Enable wrap and change the color of Listbox items
Problem description> You have a Listbox where you have a lot of items which are quite large and won't fit inside the width of your listbox. You don't want to enable Horizontal Scroll bar, since you have don't want your users to keep scrolling left and right in order to view the items. Now if you are able to achieve this, you will notice that it looks pretty ugly since you won't be able to figure out the difference between a wrapped item, and another item. So, you decide to color each item in such a way that item #1 is green, #2 is yellow, #3 is cyan, #4 is green again, and so on...
Have a look at the figure below. The first one is the normal listbox. The 2nd listbox below is the customized version. I think the 2nd one looks much better (although I guess, the color selection could have been much better)
Anyway, lets see how you can code this in VB.NET...
I have created a new class called myListBox.vb
Public Class myListBox
Inherits ListBox
Private Sub myListBox_DrawItem( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.DrawItemEventArgs _
) Handles Me.DrawItem
e.DrawBackground()
'Let's declare a brush, so that we can color the items that are added in the listbox.
Dim myBrush As Brush
If (e.State And DrawItemState.Selected) Then
e.Graphics.FillRectangle(Brushes.LightCyan, e.Bounds)
End If
'Determine the color of the brush to draw each item based on the index of the item to draw.
Select Case (e.Index) Mod 3
Case 0
myBrush = Brushes.Chocolate
Case 1
myBrush = Brushes.MediumSlateBlue
Case 2
myBrush = Brushes.Teal
End Select
' Draw the current item text based on the current Font and the custom brush settings.
e.Graphics.DrawString(Me.Items(e.Index), Me.Font, myBrush, _
New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
'If the ListBox has focus, draw a focus rectangle around the selected item.
e.DrawFocusRectangle()
End Sub
Public Sub New()
'This is super important. If you miss it... you won't be able to Draw the item.
'If you make it OwnerDrawFixed you won't be able to measure the item.
Me.DrawMode = DrawMode.OwnerDrawVariable
End Sub
Private Sub myListBox_MeasureItem( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.MeasureItemEventArgs _
) Handles Me.MeasureItem
Dim g As Graphics = e.Graphics
'We will get the size of the string which we are about to draw,
'so that we could set the ItemHeight and ItemWidth property
Dim size As SizeF = g.MeasureString(Me.Items.Item(e.Index).ToString, Me.Font, _
Me.Width - 5 - SystemInformation.VerticalScrollBarWidth)
e.ItemHeight = CInt(size.Height) + 5
e.ItemWidth = CInt(size.Width) + 5
End Sub
End Class
I have created a new form to test my listbox. Here is the code for ListBoxDemo form (Written in VS 2005 - VB.NET)
Public Class ListBoxDemo
Dim clbActualCheckedListBox As New ListBox
Dim mclbMyCheckedListBox As New myListBox
Private Sub CheckedListBoxDemo_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Height = 420
'Normal List Box
With clbActualCheckedListBox
.Top = 10
.Left = 10
.Width = 400
.Height = 150
.Font = New Font("Microsoft Sans Serif", 10, FontStyle.Bold)
.HorizontalScrollbar = True
.Items.Add("1")
.Items.Add("2")
.Items.Add("3 => This is a very very very very very very very very very very very very very very looooooooong item")
.Items.Add("4")
.Items.Add("5 => And this one is another very very very very very very very looong string")
.Items.Add("6")
End With
'Customized List Box
With mclbMyCheckedListBox
.Top = 180
.Left = 10
.Width = 400
.Height = 200
.Font = New Font("Microsoft Sans Serif", 10, FontStyle.Bold)
.HorizontalScrollbar = False
.Items.Add("1")
.Items.Add("2")
.Items.Add("3 => This is a very very very very very very very very very very very very very very looooooooong item")
.Items.Add("4")
.Items.Add("5 => And this one is another very very very very very very very looong string")
.Items.Add("6")
End With
Me.Controls.Add(clbActualCheckedListBox)
Me.Controls.Add(mclbMyCheckedListBox)
End Sub
End Class
I hope this helps!
Rahul
Share this post : |
Comments
Anonymous
March 05, 2008
Hello Rahul! When we did that code above, we got an error saying: "Value of '0' is not valid for 'index'". Can you tell us why it does that and can we fix it somehow?Anonymous
March 06, 2008
The comment has been removedAnonymous
March 06, 2008
Hi Rahul! Well, we have made some changes to the code, because all the items to our listbox comes from XML documents. The Items comes to the listbox when you load them, so they are not in the listbox immediately when you open the form...Anonymous
March 06, 2008
Hi again! We got it to work now :) Thank you for your help and for the code above :) BR 2 Girls from FinlandAnonymous
March 06, 2008
Thanks for the update and I am glad it helped. Cheers, RahulAnonymous
March 12, 2008
well-well-well.. not bad. really!Anonymous
March 14, 2008
Very nice indeed. Thanks. I have one question though. Where does the magic '5' come from when determining the size of the drawing rectangle?Anonymous
March 14, 2008
Hi DC, Thanks for your comments. In fact, that number 5 is something which I derived simply by trial and error. I was not quite happy with the effect which I get while I was using other numbers. HTH, RahulAnonymous
May 12, 2008
Good Work Mate. It helped me in Making my ListBox the way I wanted to work it. I added one property to the class so that I can tell the DrawItem Event to Change the Formatting of the Item I want to show in special format. Really a Helpful stuff, as I was show the right way.Anonymous
October 08, 2008
Awesome. I've been looking for this for a while. For some reason I had to change DrawMode.OwnerDrawVariable to System.Windows.Forms.DrawMode.OwnerDrawVariable because I was getting the Value of '0' is not valid for 'index' error but it works great now. Thanks againAnonymous
February 27, 2009
I've written this in c#, but there is a funny syntax that is not being converted right and because of it I cannot access the items in my list. I keep getting "DataGridRow" in each list. public class WrappingListBox : ListBox { public WrappingListBox() { this.DrawMode = DrawMode.OwnerDrawVariable; DrawItem += new DrawItemEventHandler(WrappingListBox_DrawItem); MeasureItem += new MeasureItemEventHandler(WrappingListBox_MeasureItem); } void WrappingListBox_DrawItem(object sender, DrawItemEventArgs e) { e.DrawBackground(); Brush mybrush; if (e.State == DrawItemState.Selected) { e.Graphics.FillRectangle(Brushes.DarkBlue, e.Bounds); } if (e.Index % 2 == 0) mybrush = Brushes.Chocolate; else mybrush = Brushes.DarkBlue; e.Graphics.DrawString(this.Items(e.Index), this.Font, mybrush, new RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)); e.DrawFocusRectangle(); } void WrappingListBox_MeasureItem(object sender, MeasureItemEventArgs e) { var g = e.Graphics; var size = g.MeasureString(this.Items.ToString(), this.Font, this.Width - 5 - SystemInformation.VerticalScrollBarWidth); e.ItemHeight = (int)size.Height + 5; e.ItemWidth = (int)size.Width + 5; } }Anonymous
June 09, 2009
Thanks for this code. It works like a charm.