Prevent a text box from lagging due to fast updates

There are a few options you can take here. First, you can set double buffering on the form, which will end up drawing all the updates on an underlying bitmap, which then displays the newly drawn image (instead of individually drawing Controls on a graphics object). I saw about a 50% speed increase with this method. Throw this into the constructor:

this.SetStyle(
  ControlStyles.AllPaintingInWmPaint |
  ControlStyles.UserPaint |
  ControlStyles.DoubleBuffer,true);

The other thing to keep in mind is that string concatenation is SLOW for large amounts of data. You're better off using a StringBuilder to build the data and then just show it using StringBuilder.ToString (although still better to stagger the updates, maybe once every 100 iterations). On my machine, just changing it to append to the StringBuilder, it went from 2.5 minutes to run through 10k iterations to about 1.5 minute. Better, but still slow.

new System.Threading.Thread(() =>
{
    for(int i = 0; i < 10000; i++)
    {
        sb.AppendLine(DateTime.Now.ToString());
        Invoke((Action)(() => 
        {
            txtArea.Text = sb.ToString();
            txtArea.SelectionStart = txtArea.Text.Length;
            txtArea.ScrollToCaret();
        }));
    }
}).Start();

Finally, just tested out staggering (threw a single conditional into the above code, right before the Invoke call), and it finished in 2 seconds. Since we're using the StringBuilder to actually build the string, we still retain all the data, but now we only have to do the updates 100 times as opposed to 10k times.

So now, what are your options? Given that this is a WinForm application, you can utilize one of the many Timer objects to actually perform the UI update for that particular control, or you can just keep a counter of how many "reads" or "updates" to the underlying data (in your case, a stream) and only update UI on X number of changes. Utilizing both the StringBuilder option and staggered updates is probably the way to go.


You could try buffering: Instead of writing directly to the TextBox and then scrolling, write to a StringBuilder (make sure you figure out how to do this in a thread-safe way!) and have a separate thread flush to the TextBox in a fixed interval (say every second).

Tags:

C#

Winforms