Raskaroth
Raskaroth

Reputation: 659

c# update listbox without 'lag'

I've been searching the web for a way to do this without much success, so here's the question.

How do add items to a listbox in a separate thread, so that it doesn't freeze up the ui? There are roughly 5-15k items each time added to the listboxes, and it freezes the ui for 5-12 seconds each time.

The form has 4 listboxes, the information for these listboxes is first created, and added to a 2D array (doing it this way makes it easier to keep track of all information that belongs together in 1 row). after which i loop over that 2D array, adding the 4 columns in 1 row to it's respective listbox.

eg.

for (int n = 0; n < 7500; n++)
{
    listBox1.Items.Add(itemList[n, 0].ToString());
    listBox2.Items.Add(itemList[n, 1].ToString());
    listBox3.Items.Add(itemList[n, 2].ToString());
    listBox4.Items.Add(itemList[n, 3].ToString());
}

As stated before, how to use a thread other than the UI to update these listboxes to prevent unnecessary freezing of the ui

Upvotes: 0

Views: 3307

Answers (7)

Dave
Dave

Reputation: 266

I just want to bring everyones answers together. You should really be updating items in your GUI thread or you may get unexpected results. To guarantee that the code is operating in the GUI thread and your list isn't repainting itself every Add() you need the BeingUpdate() and EndUpdate() pair, as others have posted, but to run that in the GUI thread use the BeingInvoke(), which simply puts the task on the "GUI thread queue" ready for consumption. BeingInvoke() will return immediate but your request will be put on the queue.

BeingInvoke() API Doc. http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx

A good discussion on BeginInvoke() and why it is used.

BeginInvoke((MethodInvoker)delegate
{
    listBox1.BeginUpdate();
    listBox2.BeginUpdate();
    listBox3.BeginUpdate();
    listBox4.BeginUpdate();
    for (int n = 0; n < 7500; n++)
    {
        listBox1.Items.Add(itemList[n, 0].ToString());
        listBox2.Items.Add(itemList[n, 1].ToString());
        listBox3.Items.Add(itemList[n, 2].ToString());
        listBox4.Items.Add(itemList[n, 3].ToString());
    }
    listBox1.EndUpdate();
    listBox2.EndUpdate();
    listBox3.EndUpdate();
    listBox4.EndUpdate();
});

Upvotes: 0

Sedat Kapanoglu
Sedat Kapanoglu

Reputation: 47690

You can take a different approach and use a virtual ListView instead. When a ListView is "virtual", you are responsible for maintaining item lists and telling ListView what to display on paint events. This way you can update your lists in any thread-safe way you'd like and ListView will only ask of you "what to draw on screen" rather than a full list of items. That's actually the preferred method when obtaining list of all items is very costly.

See the documentation on VirtualMode:

http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.virtualmode.aspx

UPDATE: Since you mentioned that you need it for debugging I also suggest you to use debug output (Debug.WriteLine()) which could be more suitable for the job. It's optimized, it's thread safe, it doesn't block anything but itself and the best part is it doesn't affect performance of release builds.

And in case you need a more performant output you can redirect your debugging output to anywhere you like.

Upvotes: 4

SLaks
SLaks

Reputation: 888203

That's inherently impossible.
You can only manipulate the UI from the UI thread. (UI is not thread-safe)

You can make it faster by calling BeginUpdate() and EndUpdate(), and you can make it even faster (but harder) by using a ListView in virtual mode.

However, you should not display 15,000 items in a lsitbox.
Such a listbox would be useless in actual use.

Upvotes: 2

Robert Rouhani
Robert Rouhani

Reputation: 14688

You can significantly speed up this process by preventing the ListBox from repainting after every add by wrapping the for loop in:

listBox1.BeginUpdate();
listBox2.BeginUpdate();
listBox3.BeginUpdate();
listBox4.BeginUpdate();
for (int n = 0; n < 7500; n++)
{
    listBox1.Items.Add(itemList[n, 0].ToString());
    listBox2.Items.Add(itemList[n, 1].ToString());
    listBox3.Items.Add(itemList[n, 2].ToString());
    listBox4.Items.Add(itemList[n, 3].ToString());
}
listBox1.EndUpdate();
listBox2.EndUpdate();
listBox3.EndUpdate();
listBox4.EndUpdate();

Upvotes: 1

ken2k
ken2k

Reputation: 49013

You should try to use BeginUpdate/EndUpdate methods:

listbox1.BeginUpdate();
// Adds 5K items
listbox1.EndUpdate();

Upvotes: 1

Brad
Brad

Reputation: 10690

listBox1.BeginUpdate();
listBox2.BeginUpdate();
listBox3.BeginUpdate();
listBox4.BeginUpdate();

for (int n = 0; n < 7500; n++)
{
    listBox1.Items.Add(itemList[n, 0].ToString());
    listBox2.Items.Add(itemList[n, 1].ToString());
    listBox3.Items.Add(itemList[n, 2].ToString());
    listBox4.Items.Add(itemList[n, 3].ToString());
}


listBox1.EndUpdate();
listBox2.EndUpdate();
listBox3.EndUpdate();
listBox4.EndUpdate();

This will delay drawing until all items have been added, so it should be faster.

Upvotes: 1

Related Questions