<< Applying XP visual styles to Windows Forms applications | Home | Absolute coordinates of DOM element within document >>

Update ListBox item in Windows Forms

Problem description

Sometimes in Windows Forms applications you use the following code pattern to fill some ListBox control with necessary data:

public class MyObject {
		
	private string text;

	public string Text {
		get { return text; }
		set { text = value; }
	}

	public MyObject(string text) {
		this.text = text;
	}
		

	public override string ToString() {
		return this.text;
	}

}

.  .  .  .  .  .  .

	myListBox.Items.Add(new MyObject("Item 1"));
	myListBox.Items.Add(new MyObject("Item 2"));
	myListBox.Items.Add(new MyObject("Item 3"));
.  .  .  .  .  .  .

Now let's suppose you need to change some item and set new value to it's Text property:
.  .  .  .  .  .  .
	MyObject item = (MyObject)myListBox.Items[0];
	item.Text = "New value";
	//Corresponding item in list box is not updated accordingly.
.  .  .  .  .  .  .
However the corresponding item in your ListBox will not reflect to such change even if you manually "tell" the ListBox to update (through Update or Refresh methods).

The solution

The right solution fot this problem is to use data binding (you can read this MSDN article for more information). But very often it is too complicated way for such simple situation so I would like to propose you some gimmick which allows to solve the problem without all that binding stuff (and it coud work even faster in most cases).
We just need to re-assign modified object to necessary item in the list. So I have wrotten the following simple function:
	private void UpdateListBoxItem(ListBox lb, object item) {
		int index = lb.Items.IndexOf(item);
		int currIndex = lb.SelectedIndex;
		lb.BeginUpdate();
		try {
			lb.ClearSelected();
			lb.Items[index] = item;
			lb.SelectedIndex = currIndex;
		}
		finally {
			lb.EndUpdate();
		}
	}
Now when I need to update modified item in the list I just call this function with my ListBox object at first parameter and an update item object in second:
.  .  .  .  .  .  .
	MyObject item = (MyObject)myListBox.Items[0];
	item.Text = "New value";
	UpdateListBoxItem(myListBox, item);
.  .  .  .  .  .  .
As I said before, this is not quite correct solution but it does what we need and it helped me in many cases.


Re: Update ListBox item in Windows Forms

For me, reassigning the object to the Items list just worked fine. So whenever something changed in my displayed objects, I reassign it:

For example:

class MyObj
{
  public string Name { get; set; }
};

MyListBox.DisplayMember = "Name";

and when the object's name is changed:

var obj = MyListBox.Items[MyListBox.SelectedIndex];
obj.Name = "New Name";
MyListBox.Items[index] = obj;

Probably it works for your example, too.

best regards
...armin.

Re: Update ListBox item in Windows Forms

Actually you are right.
Re-assigning works well too.
But the rest of code is still necessary because otherwise some problems can occur if you call UpdateListBoxItem function inside SelectedIndexChange event handler.

I have modified the code so now the following two rows:
  lb.Items.RemoveAt(index);
  lb.Items.Insert(index, item);
are replaced by the one you proposed:
  lb.Items[index] = item;

Re: Update ListBox item in Windows Forms

I can see by using Reflector, that updating the Item actually does automatic deselection and reselection. But the implementation causes an other serious problem. Noone would expect SelectedIndexChanged to be fired on an item update! But this is exactly what happens, because an Item update is also implemented by an internal remove and add operation :(

I solved the problem by globally flagging item updates and hacking my SelectedIndexChanged handlers, but to create a fine solution, one has to derive from ListBox and override OnSelectedIndexChanged() and probably RefreshItem() or so.

Add a comment Send a TrackBack