Compartir a través de


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

  • 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 removed

  • Anonymous
    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 Finland

  • Anonymous
    March 06, 2008
    Thanks for the update and I am glad it helped. Cheers, Rahul

  • Anonymous
    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, Rahul

  • Anonymous
    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 again

  • Anonymous
    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.