Reputation: 659
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
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
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
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
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
Reputation: 49013
You should try to use BeginUpdate
/EndUpdate
methods:
listbox1.BeginUpdate();
// Adds 5K items
listbox1.EndUpdate();
Upvotes: 1
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
Reputation: 3097
Use BeginUpdate()
and EndUpdate()
http://msdn.microsoft.com/en-us/library/system.windows.forms.listbox.beginupdate.aspx
Upvotes: 2