views:

227

answers:

4

I populate a ListBox control with my own objects redefining ToString(). The objects are displayed correctly when I just add those objects using listBox1.Add(myObject). However, if I later change something in this object, no changes are displayed in the listbox. Debugging reveals that an object inside listBox1.Items is indeed changed, but it is not reflected on a screen.

Interestingly enough, if I reassign a particular listbox item to itself (sounds a bit weird, doesn't it?), like:

listBox1.Items[0] = listBox1.Items[0]

this line will display a correct value on screen.

What is going on here? Does it have anything to do with threading?

A: 

you could invalidate the control, forcing a re-paint... perhaps..

Sean.C
+1  A: 

Have you tried calling Refresh() on the ListBox? I think the problem is that the ListBox does not know your object changed. The reason reassigning the the item works is because the ListBox will repaint itself when the collection changes.

Zach Johnson
Actually Refresh just invalidates and repaints the control. I doubt this will have any effect on what ListBox displays. You can achieve the same thing by dragging a window in front of the ListBox then off of it. The real issue is that ListBoxItem is caching the ToString value because no DisplayMember is set.
Josh Einstein
+1  A: 

Since you're using ToString of the object to provide the text of the list box item, the ListBox has no idea that the value has changed. What you should do instead is have the object implement INotifyPropertyChanged then expose a public property such as Name or Text and return what you normally would have returned from ToString().

Then set the DisplayMember of the ListBox to the name of the new property.

Make sure you are correctly raising the PropertyChanged event in the object and the ListBox should be able to automatically pick up the changes.

Edit: Adrian's edit reminded me that I do believe you'll need to use a BindingList as your data source in order for the property change notifications to be picked up. A quick scan in Reflector looks like ListBox on its own will not pick up the property changes mentioned above. But INotifyPropertyChanged + BindingList should.

Josh Einstein
+1  A: 

The ToString() value of each item is cached when the listbox is first displayed. If an item in the listbox's Items collection then changes, the listbox does not notice and still uses the cached ToString() values for display. To force the listbox to update, either call RefreshItems() to refresh all items, or call RefreshItem(int) specifying the index of the item to refresh.

From the MSDN docs for RefreshItems():

Refreshes all ListBox items and retrieves new strings for them.


EDIT: It turns out that both of these methods are protected, so cannot be called externally. In trying to find a solution, I came across this SO question that this question is basically a duplicate of.

adrianbanks
Both methods are protected and are not intended to be called externally.
Josh Einstein
@Josh: So they are! I hadn't noticed that in the MSDN docs.
adrianbanks