L
larrylard
Searching google groups for 'listbox items redraw' yields lots of
people with the same problem, but little in the way of solutions.
The problem: A ListBox's Items collection can contain arbitrary
objects, which is wonderful, and the listbox will decide what to
display for each item by calling its ToString. This means we don't need
Tags, we don't need ItemData, all great.
However.
When one of those Item object changes in such a way that its ToString
changes - there is apparently NO WAY to tell the listbox about this.
Example:
Public Class SomeClass
Public SomeData As Double
Public Sub New()
End Sub
Public Sub New(ByVal d As Double)
Me.SomeData = d
End Sub
Public Overrides Function ToString() As String
Dim a() As Char = SomeData.ToString.ToCharArray
Array.Reverse(a)
Return New String(a)
End Function
End Class
A straightforward demonstration class with a ToString that depends on
internal state. Yes I know public members are bad. This is an example
Create a form, put a listbox ListBox1 on it, and three Buttons, Button1
Button2 Button3. In the form:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
With ListBox1.Items
.Clear()
.Add(New SomeClass(3.45#))
.Add(New SomeClass(4.435345#))
.Add(New SomeClass(Math.PI))
End With
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button2.Click
Dim o As SomeClass = DirectCast(ListBox1.Items(1), SomeClass)
o.SomeData *= 2
'WHAT NOW?
ListBox1.Refresh()
ListBox1.Invalidate()
ListBox1.Update()
'none of the above have any effect
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button3.Click
Dim al As ArrayList = New ArrayList
For Each o As Object In ListBox1.Items
al.Add(o)
Next
With ListBox1.Items
.Clear()
For Each o As Object In al
.Add(o)
Next
End With
End Sub
When you run this, click Button1 to have some SomeClass's put into the
list box. When you click Button2, one of those SomeClass's *changes*
and its ToString changes too, but (as you will see), .Refresh,
..Invalidate, .Update - all fail to persude the listbox to go get the
ToString again.
Button3 removes then adds back all the items in the list box - this
DOES force the listbox to check their ToString's, and enables you to
see the updated SomeClass.
Removing and restoring the single affected SomeClass also works, but
with rather unpleasant side effects on ordering, selectedness, etc etc.
So, any bright ideas as to how to tell the listbox that one of its
Item's ToString has changed? Remember, these DON'T work:
..Refresh
..Update
..Invalidate
people with the same problem, but little in the way of solutions.
The problem: A ListBox's Items collection can contain arbitrary
objects, which is wonderful, and the listbox will decide what to
display for each item by calling its ToString. This means we don't need
Tags, we don't need ItemData, all great.
However.
When one of those Item object changes in such a way that its ToString
changes - there is apparently NO WAY to tell the listbox about this.
Example:
Public Class SomeClass
Public SomeData As Double
Public Sub New()
End Sub
Public Sub New(ByVal d As Double)
Me.SomeData = d
End Sub
Public Overrides Function ToString() As String
Dim a() As Char = SomeData.ToString.ToCharArray
Array.Reverse(a)
Return New String(a)
End Function
End Class
A straightforward demonstration class with a ToString that depends on
internal state. Yes I know public members are bad. This is an example
Create a form, put a listbox ListBox1 on it, and three Buttons, Button1
Button2 Button3. In the form:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
With ListBox1.Items
.Clear()
.Add(New SomeClass(3.45#))
.Add(New SomeClass(4.435345#))
.Add(New SomeClass(Math.PI))
End With
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button2.Click
Dim o As SomeClass = DirectCast(ListBox1.Items(1), SomeClass)
o.SomeData *= 2
'WHAT NOW?
ListBox1.Refresh()
ListBox1.Invalidate()
ListBox1.Update()
'none of the above have any effect
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button3.Click
Dim al As ArrayList = New ArrayList
For Each o As Object In ListBox1.Items
al.Add(o)
Next
With ListBox1.Items
.Clear()
For Each o As Object In al
.Add(o)
Next
End With
End Sub
When you run this, click Button1 to have some SomeClass's put into the
list box. When you click Button2, one of those SomeClass's *changes*
and its ToString changes too, but (as you will see), .Refresh,
..Invalidate, .Update - all fail to persude the listbox to go get the
ToString again.
Button3 removes then adds back all the items in the list box - this
DOES force the listbox to check their ToString's, and enables you to
see the updated SomeClass.
Removing and restoring the single affected SomeClass also works, but
with rather unpleasant side effects on ordering, selectedness, etc etc.
So, any bright ideas as to how to tell the listbox that one of its
Item's ToString has changed? Remember, these DON'T work:
..Refresh
..Update
..Invalidate