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) Smile

 image

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! Smile
Rahul


Share this post :

Comments (12)
  1. aXit says:

    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?

  2. imRahulSoni says:

    Hi aXit,

    It seems like the items are not present in the Listbox. Did you ensure that you have followed the steps in this blog fully?

    I have checked it on my box, and it seems to be working fine.

    HTH,

    Rahul

  3. aXit says:

    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…

  4. aXit says:

    Hi again!

    We got it to work now 🙂

    Thank you for your help and for the code above 🙂

    BR

    2 Girls from Finland

  5. imRahulSoni says:

    Thanks for the update and I am glad it helped.

    Cheers,

    Rahul

  6. ... says:

    well-well-well.. not bad. really!

  7. dc says:

    Very nice indeed.  Thanks.  I have one question though.  Where does the magic ‘5’ come from when determining the size of the drawing rectangle?

  8. imRahulSoni says:

    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,

    Rahul

  9. kumaravasthi says:

    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.

  10. Brian says:

    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 again

  11. Ethan says:

    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;

           }

       }

  12. Mr Hulk says:

    Thanks for this code. It works like a charm.

Comments are closed.

Skip to main content