Click here to Skip to main content
15,886,026 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Does anyone know why the Listbox1.Refresh() command may not trigger the ListBox1_DrawItem sub every time?

In Microsoft Visual Basic 2010, a listbox has a forcolor and backcolor property. These properties change the forcolour and backcolor for all the items in the listbox. By default there is no property for the forecolor and backcolor of an individual item on a listbox, I am aware there is on a list view but I would still wish to use a listbox. I am trying to have the ability to change the forecolor and backcolor properties of individual items in the listbox. To do this the listbox's draw item sub must be used with the listbox's drawmode property set to OwnerDrawFixed. Then using a brush colour along with the e.graphics the forecolor or backcolor can be changed. I have seen and followed examples of how to do this for the currently selected item. Such as the one from ehow's website. What I am tiring to do however is change the colour of the litsbox item as it is added depending on a variable.

Here is my code:
VB
Private Sub listbox_add()

        Me.ListBox1.Items.Add(listbox_text(list_num)) ' adds the line to the list box
        add_item_colour = True
        ListBox1.Refresh()

End Sub



    Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    Dim myBrush As Brush = Brushes.Black

    e.DrawBackground()
    If add_item_colour = True Then
        If blue_message = True Then
            myBrush = Brushes.Blue
        Else
            myBrush = Brushes.Black
        End If
        e.Graphics.DrawString(ListBox1.Items.Item(list_num), ListBox1.Font, myBrush, _
        New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
        add_item_colour = False
    End If
    e.DrawFocusRectangle()
End Sub


The listbox_text is a string array that stores the string being added, the list_num is a integer that increments when new items are added to the listbox and the blue_message is a Boolean that it true when I want a blue message and false when I don't.

The problem I seem to be having is that Listbox1.Refresh() command does not seem to be triggering the ListBox1_DrawItem sub every time it is called. I found this by using brake points. Does anyone know why this might be the case and how I could fix it?

Thanks, any help on this would be much appreciated.
Posted
Updated 8-Sep-20 2:09am
Comments
Richard C Bishop 3-Sep-13 10:24am    
Why do you think calling the .Refresh() method will run a different method?
Member 10190163 3-Sep-13 10:30am    
Because I was under the impression that the refresh would force the listbox to be re-drawn, meaning the ListBox1_DrawItem sub would be called. Is this not the case?
Richard C Bishop 3-Sep-13 11:36am    
It does redraw itself. Right click on the .Refresh() method and "Go to Definition". That is the code it runs.

1 solution

Successful Example - Revised 5 Sep 2013 10:57 EDT to include RemoveAt
The following code works.
I tested using Visual Studio 2012 on a Windows 8 PC.

By using the Visual Studio Interactive Debugger, I determined that all visible rows in the ListBox are refreshed when a new row is added and that if a new row is not visible, it is not drawn. Therefore, I had to save the brush color for each row in a collection before adding the new row and use that brush color each time the associated ListBox row is drawn. I use Collection Class to save the brush colors for each row of the ListBox.

The Refresh method was not needed.

VB
Dim colColors As Collection = New Collection ' Space to save brush colors for each row in the ListBox

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    ListBox1.DrawMode = DrawMode.OwnerDrawFixed
End Sub

Private Sub listbox_add_Blue()
    SetColorForNewRow(Brushes.Blue)
    ListBox1.Items.Add("Blue") ' adds the line to the list box
End Sub

Private Sub listbox_add_Black()
    SetColorForNewRow(Brushes.Black)
    ListBox1.Items.Add("Black") ' adds the line to the list box
End Sub

Sub listbox_remove_0()
    If ListBox1.Items.Count < 1 Then Exit Sub
    ' Must remove brush color from colColors collection first!
    colColors.Remove(1) ' Collection index starts at 1
    ListBox1.Items.RemoveAt(0)
End Sub

Private Sub ListBox1_DrawItem(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    e.DrawBackground()
    If e.Index >= 0 AndAlso e.Index < ListBox1.Items.Count Then
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), _
            e.Font, colColors.Item(1 + e.Index), _
            e.Bounds, StringFormat.GenericDefault)
    End If
    e.DrawFocusRectangle()
End Sub

Sub SetColorForNewRow(ByVal MyBrush As Brush)
    colColors.Add(MyBrush)
End Sub

Private Sub btnBlack_Click(sender As Object, e As EventArgs) Handles btnBlack.Click
    listbox_add_Black()
End Sub

Private Sub btnBlue_Click(sender As Object, e As EventArgs) Handles btnBlue.Click
    listbox_add_Blue()
End Sub

Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
    listbox_remove_0()
End Sub




_____________________________________________________________________________________________
First attempt using an array to store brush colors (Partial Success)
The following code works for small number of items in the ListBox. I tested using Visual Studio 2012 on a Windows 8 PC. I am still studying how to handle the DrawItem event when the vertical scroll bar is active.

By using the Visual Studio Interactive Debugger, I determined that all rows in the ListBox are refreshed when a new row is added. Therefore, I had to save the brush color for each row in an array and use that brush color each time the associated ListBox row is redrawn. The Refresh method was not needed.

This would have been a lot easier with a ListView control.

VB
Dim blue_message As Boolean = False ' Use Black or Blue Brush
Dim AddMode As Boolean = False  ' Tells DrawItem that it is in Add to ListBox mode
Dim intNewIndex As Integer ' The index of the new item to be added to the ListBox
Dim arrColors(0 To 100) As Brush ' Space to save brush colors for each row in the ListBox

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    ListBox1.DrawMode = DrawMode.OwnerDrawFixed
End Sub

Private Sub listbox_add_Blue()
    blue_message = True
    AddMode = True
    intNewIndex = ListBox1.Items.Count
    ListBox1.Items.Add("Blue") ' adds the line to the list box
End Sub

Private Sub listbox_add_Black()
    blue_message = False
    AddMode = True
    intNewIndex = ListBox1.Items.Count
    ListBox1.Items.Add("Black") ' adds the line to the list box
End Sub

Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    Dim myBrush As Brush = Brushes.Black
    e.DrawBackground()
    If AddMode AndAlso e.Index = intNewIndex Then
        If blue_message Then
            myBrush = Brushes.Blue
        End If
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), e.Font, myBrush, _
            e.Bounds, StringFormat.GenericDefault)
        If e.Index > arrColors.GetUpperBound(0) Then ' Is the array filled
            ReDim Preserve arrColors(0 To arrColors.Count + 100) ' Make some more room
        End If
        arrColors(e.Index) = myBrush ' Save which brush we used
        AddMode = False ' Finished drawing the new item (It is the last row of the ListBox
    Else
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), _
            e.Font, arrColors(e.Index), _
            e.Bounds, StringFormat.GenericDefault)
    End If
    e.DrawFocusRectangle()
End Sub

Private Sub btnBlack_Click(sender As Object, e As EventArgs) Handles btnBlack.Click
    listbox_add_Black()
End Sub

Private Sub btnBlue_Click(sender As Object, e As EventArgs) Handles btnBlue.Click
    listbox_add_Blue()
End Sub



_____________________________________________________________________________________________
Original Version of the Solution
I looked at the Microsoft Help page for ListBox.DrawItem event and made a few changes to e.Graphics.DrawString statement. Also, notice that I moved the location of add_item_colour = True.

I did not test.

VB
Private Sub listbox_add()
        add_item_colour = True
        Me.ListBox1.Items.Add(listbox_text(list_num)) ' adds the line to the list box
        ListBox1.Refresh()
End Sub

    Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    Dim myBrush As Brush = Brushes.Black

    e.DrawBackground()
    If add_item_colour = True Then
        If blue_message = True Then
            myBrush = Brushes.Blue
        Else
            myBrush = Brushes.Black
        End If
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), e.Font, myBrush, _
            e.Bounds, StringFormat.GenericDefault)
        add_item_colour = False
    End If
    e.DrawFocusRectangle()
End Sub
 
Share this answer
 
v25
Comments
Member 10190163 4-Sep-13 4:51am    
Thanks for the suggestion, but unfortunately it did not work.
I got an exception every time something was added: A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
Also I saw some black and blue items being added, but the problem was it only added them to the first item then they were cleared.
Member 10190163 4-Sep-13 4:53am    
I have also tied it with this line: e.Graphics.DrawString(ListBox1.Items.Item(list_num).ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault)
just in-case this fixes the problem of it being added only to the first item and cleared, but again I had the same problem.
Member 10190163 4-Sep-13 4:56am    
Also if you are going to test it, you need to remember to change the draw mode property on the listbox.
Mike Meinz 4-Sep-13 9:35am    
See revised Solution 1. It took me two tries to get a version of the solution that work correctly. My first version did not work correctly when there was a vertical scroll bar present. See the comments within Solution 1.
Member 10190163 4-Sep-13 11:45am    
I am using Windows 7 and Microsoft Visual Basic 2010 Express. I have created a new project and tried to apply your code to this. I have a problem with the line:
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), e.Font, arrColors(e.Index), e.Bounds, StringFormat.GenericDefault)
It causes an exception when press run and I can not continue with the program because it brings up a dialog box that lets me brake or continue and when I continue the debugger stops. The dialog box messages is as follows: An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in System.Windows.Forms.dll
Additional information: InvalidArgument=Value of '-1' is not valid for 'index'.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900