c# update listbox without 'lag' - c#

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

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.

Use BeginUpdate() and EndUpdate()
http://msdn.microsoft.com/en-us/library/system.windows.forms.listbox.beginupdate.aspx

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.

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.

You should try to use BeginUpdate/EndUpdate methods:
listbox1.BeginUpdate();
// Adds 5K items
listbox1.EndUpdate();

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();

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();
});

Related

Operations in background with UI reaction

I need to make a sorting application that displays the current operations.
I've done it like that:
I have collection of ints. Inside it, I have sorting algorithms and event 'step' that takes two arguments 'operation' eg. compare, permutation etc. and int[] of elements that I do something with.
I want to change the item background in ListView if sorting is doing something with specific items. '250 ms blink'. Something like that:
listView1.Items[1].BackColor = Color.LightGreen;
listView1.Items[2].BackColor = Color.LightGreen;
Thread.Sleep(250);
for (int i = 0; i < args.Length; i++)
{
listView1.Items[i].BackColor = Color.White;
}
But when I'm trying to start sorting, the UI is freezing and after couple of seconds it is displayed as a SortedCollection.
My question is:
How do I show 'live' actions on UI during sorting?
Instead of Thread.Sleep(250) use await Task.Delay(250). To make this possible you need to mark your method as async

Is there a faster way to print to a TextBox than AppendText?

I have a background thread that is reading messages from a device and formatting them and passing them to the 'Consumer' node of a Producer/Consumer collection that then prints all of the messages to the string. The problem I am running into is that the logging lags a little bit behind the end of the process, so I am trying to find a faster way to print to the screen. Here is my consumer method:
private void DisplayMessages(BlockingCollection<string[]> messages)
{
try
{
foreach (var item in messages.GetConsumingEnumerable(_cancellationTokenSource.Token))
{
this.Invoke((MethodInvoker)delegate
{
outputTextBox.AppendText(String.Join(Environment.NewLine, item) + Environment.NewLine);
});
}
}
catch (OperationCanceledException)
{
//TODO:
}
}
I have done some benchmark tests on my producer methods, and even logging to the console, and it does appear that writing to this TextBox is what is a little slower. During each process, I am logging ~61,000 lines that are about 60 characters long.
I have researched that it is better to use .AppendText() than to say textBox.Text += newText as this resets the entire TextBox's text. I am looking for a solution that may include a faster way to print to a TB (or a UI element better suited for quick logging?) or if using String.Join(Environment.NewLine, item) is inefficient and could be sped up in any way.
Code with O(n^2) performance is expected to be slow.
If you can't use String.Join to construct whole output once then consider using list-oriented controls or even grids. If you have very large number of rows most list controls and grids have "virtual" mode where text is actually requested on demand.
String.Join isn't inefficient. The way it is called though causes it to be called N times (once for each message arrau in the collection) instead of just once. You could avoid this by flattening the collection with SelectMany. SelectMany takes an IEnumerable as input and returns each individual item in it:
var allLines=messages.SelectMany(msg=>msg);
var bigText=String.Join(allLines,Environment.NewLine);
Working with such a big text box though will be very difficult. You should consider using a virtualized grid or listbox, adding filters and search functionality to make it easier for users to find the messages they want.
Another option may be to simply assign the lines to the TextBox.Lines property without creating a string:
var allLines=messages.SelectMany(msg=>msg);
outputTextBox.Lines=allLines.ToArray();

Wait until task has completed within a loop before executing next line of code

I am using Visual C# 2013 to create a win-forms applications. I have a loop which takes up to a minute to complete due to a large calculation having to be carried out on many rows of data within a table, therefore whilst the user waits I display a 'loading...' form.
On this form I wish to display a count down of the number of rows, so the user can see how many rows of the data there are left to be calculated, however the label with this number on will not update as everything 'freezes' until the loop has finished.
System.Windows.Forms.Form f = System.Windows.Forms.Application.OpenForms["LoadingForm"];
int DataRowsRemaining = TotalRowCount;
for (int i=0; i<=TotalRowCount; i++)
{
//CALCULATION CODE
((LoadingForm)f).label1.Text = Convert.ToString(DataRowsRemaining--);
}
This code does not allow the label to update during the loop. Using, Application.DoEvents(); after the label does allow it to be updated but this also refreshes every other label on the form which significantly slows down the calculation, therefore I think I need to allow this one line of code to be carried out on a separate thread.
Due to my knowledge being limited on the subject, could anyone advise me whether the multi-threading technique would be the best way to solve this issue, and if so any advice on how I could code this as I have been struggling to understand online examples of multi-threading.
Thanks for your time, Aaron.
Simplest way would be
await Task.Run(() => { /* CALCULATION CODE */ });
private async void Calculation()
{
for (int i = 0; i <= TotalRowCount; i++)
{
await Task.Run(() => { /* CALCULATION CODE */ });
((LoadingForm)f).label1.Text = Convert.ToString(DataRowsRemaining--);
}
}
There is an example here in MSDN on how to use .Invoke(). However, L.B's answer is probably much simpler.

C# - updating label while adding items to ListView

I have a ListView which I populate with a lot of items, over 3000. This can take up to 15 seconds.
Every time I add an item I want to update a label stating how many items have been added so far.
To do so I use this code:
foreach (FileInfo f in dir.GetFiles("*.*", SearchOption.AllDirectories))
{
DateTime dt = GetDateTakenFromImage(Path.Combine(f.Directory.ToString(), f.Name));
count++;
labelLoadedLeft.Text = "Loading " + count + " files so far";
ListViewItem lSingleItem = lv.Items.Add(f.Name);
lSingleItem.SubItems.Add(dt.ToString("dd MMMM yyyy"));
lSingleItem.Tag = Path.Combine(f.Directory.ToString(), f.Name);
}
Unfortunately the label does not show until all items have been loaded.
I understand this has to do with the fact that I am doing a lengthy operation on thr UI thread and that I should probably be using a backgroundworker to do the work.
Does anyone know of good and simple examples on how to use background worker. What I have found so far is too complicated for me or too convoluted.
Thank you
Crouz
If you only want your Label to update, you should update it.
labelLoadedLeft.Text = "Loading " + count + " files so far";
labelLoadedLeft.Update();
Which version of VS/C# do you use? With VS2012/C#5.0 you could take advantage of the new "await" feature. It makes code easy to read and updating the UI can be done without invoke etc
You can do it with a BackgroundWorker, here's an example, http://dotnetforum.net/topic/34729-how-to-cancel-backgroundworker-during-getfiles/.
However, the UI will never show anything until the you get a list of all files. And as you said this is slow.
Use the EnumerateFiles method, http://msdn.microsoft.com/en-us/library/dd383458(v=vs.100).aspx, which returns one file at a time.
A background worker is most easily handled via the TPL, presuming you're working with .NET 4.0. You can start a worker in the background using code that looks like this:
Task.Factory.StartNew( () =>
{
// Background Worker Stuff goes here
}, TaskCreationOptions.LongRunning);
You can then put your worker's job in another class/function, and have it yield return its results from DoWork(), giving you an IEnumerable<T> of whatever it's returning. Using your foreach loop over that like so:
foreach(var item in DoWork())
{
// Update UI
}
Will mean you can update the UI as results come back. Yield return will allow you to handle items before the entire enumerable returns. Be careful, though - yield return implicitly sets up a state machine inside your program, so unless you need functionality like this it can be very wasteful to use this idiom.
You may not need it. Use Begin/EndUpdate on the ListView.
http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.beginupdate.aspx
This will prevent drawing of the items until they're all loaded. You won't be able to maintain a count, but I don't really see the benefit of that at all, especially if this makes it too fast to matter.

how to increase speed of my execution

i am creating project in c#.net. my execution process is very slow. i also found the reason for that.in one method i copied the values from one list to another.that list consists more 3000values for every row . how can i speed up this process.any body help me
for (int i = 0; i < rectTristrip.NofStrips; i++)
{
VertexList verList = new VertexList();
verList = rectTristrip.Strip[i];
GraphicsPath rectPath4 = verList.TristripToGraphicsPath();
for (int j = 0; j < rectPath4.PointCount; j++)
{
pointList.Add(rectPath4.PathPoints[j]);
}
}
This is the code slow up my procees.Rect tristirp consists lot of vertices each vertices has more 3000 values..
A profiler will tell you exactly how much time is spent on which lines and which are most important to optimize. Red-gate makes a very good one.
http://www.red-gate.com/products/ants_performance_profiler/index.htm
Like musicfreak already mentioned you should profile your code to get reliable result on what's going on. But some processes are just taking some time.
In some way you can't get rid of them, they must be done. The question is just: When they are neccessary? So maybe you can put them into some initialization phase or into another thread which will compute the results for you, while your GUI is accessible to your users.
In one of my applications i make a big query against a SQL Server. This task takes a while (built up connection, send query, wait for result, putting result into a data table, making some calculations on my own, presenting the results to the user). All of these steps are necessary and can't be make any faster. But they will be done in another thread while the user sees in the result window a 'Please wait' with a progress bar. In the meantime the user can already make some other settings in the UI (if he likes). So the UI is responsive and the user has no big problem to wait a few seconds.
So this is not a real answer, but maybe it gives you some ideas on how to solve your problem.
You can split the load into a couple of worker threads, say 3 threads each dealing with 1000 elements.
You can synchronize it with AutoResetEvent
Some suggestions, even though I think the bulk of the work is in TristripToGraphicsPath():
// Use rectTristrip.Strip.Length instead of NoOfStrips
// to let the JIT eliminate bounds checking
// .Count if it is a list instead of array
for (int i = 0; i < rectTristrip.Strip.Length; i++)
{
VertexList verList = rectTristrip.Strip[i]; // Removed 'new'
GraphicsPath rectPath4 = verList.TristripToGraphicsPath();
// Assuming pointList is infact a list, do this:
pointList.AddRange(rectPath4.PathPoints);
// Else do this:
// Use PathPoints.Length instead of PointCount
// to let the JIT eliminate bounds checking
for (int j = 0; j < rectPath4.PathPoints.Length; j++)
{
pointList.Add(rectPath4.PathPoints[j]);
}
}
And maybe verList = rectTristrip.Strip[i]; // Removed 'VertexList' to save some memory
Define variable VertexList verList above loop.

Categories