views:

480

answers:

4

Hello. I have a C# application in which a LOT of information is being added to a Textbox for display to the user. Upon processing of the data, almost immediately, the application becomes very slow and unresponsive. This is how I am currently attempting to handle this:

   var saLines = textBox1.Lines;
   var saNewLines = saLines.Skip(50);
   textBox1.Lines = saNewLines.ToArray();

This code is run from a timer every 100mS. Is there a better way to handle this? I am using Microsoft Visual C# 2008 Express Edition. Thanks.

A: 

Try by having in memory a list with the content, and removing the first 50 elements by RemoveRange and then going with ToArray(); Like this :

lst.RemoveRange(0,50);
textBox1.Lines = lst.ToArray();

It should be a lot faster.

feal87
A: 

I'd say your main problem here is that you are using the TextBox as your primary storage for your text. Everytime you call TextBox.Lines, the string is split on Environment.NewLine.

Try turning it around:

  • Store the text in a new List<String>(maxLines)
  • In your AddLine method, check the length of your text buffer and use RemoveRange(0, excessCount)
  • Update your display TextBox by calling String.Join(Environment.NewLine, textBuffer.ToArray())

That last call is a bit expensive, but it should stop your slowdowns. To get it any faster you'd need to use a statically sized string array and move the references around yourself.

Richard Szalay
A: 

The most efficient way to trim an array is to create a new array of the desired size, then use Array.Copy to copy the desired portion of the old array.


I would recommend that you maintain a List<string> containing all of your lines. You should use a StringBuilder to build a string containing the lines you're looking for, and set the textbox's Text proeprty to the StringBuilder's string. For added performance, set the StringBuilder's capacity to a reasonable guess of the final sie of the string. (Or to list.Skip(...).Take(...).Sum(s => s.Length))

If you're concerned about memory, you can trim the List<string> by calling RemoveRange.

As long as you don't put too much in the textbox at once, doing it this way should be extremely fast. All of the manipulation of the List<string> and the StringBuilder can be done in a background thread, and you can pass the completed string to the UI thread.

The TextBox.Lines property simply concatenates the array you give it using a StringBuilder, so there's no point in using it (and making a needless array).

SLaks
+3  A: 

The simple answer is TextBox.AppendText().

You get much better performance initially. I tested writing a 500 char message every 20 ms for 2 mins (with BackgroundWorker) and the UI remained responsive and CPU minimal. At some point, of course, it will become unresponsive but it was good enough for my needs.

Peter Goras
Thank you. Some months later I was able to get this working by building up global `StringBuilder` objects in the `backgroundWorker` and then using `TextBox.AppendText()` in the `backgroundWorkerReportProgress` and `backgroundWorkerRunWorkerCompleted` methods.
Jim Fell