Saturday, May 10, 2008

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.