My WinForms application has a TextBox that I'm using as a log file. I'm appending text without the form flickering using TextBox.AppendText(string);
, however when I try to purge old text (as the control's .Text property reaches the .MaxLength limit), I get awful flicker.
The code I'm using is as follows:
public static void AddTextToConsoleThreadSafe(TextBox textBox, string text)
{
if (textBox.InvokeRequired)
{
textBox.Invoke(new AddTextToConsoleThreadSafeDelegate(AddTextToConsoleThreadSafe), new object[] { textBox, text });
}
else
{
// Ensure that text is purged from the top of the textbox
// if the amount of text in the box is approaching the
// MaxLength property of the control
if (textBox.Text.Length + text.Length > textBox.MaxLength)
{
int cr = textBox.Text.IndexOf("\r\n");
if (cr > 0)
{
textBox.Select(0, cr + 1);
textBox.SelectedText = string.Empty;
}
else
{
textBox.Select(0, text.Length);
}
}
// Append the new text, move the caret to the end of the
// text, and ensure the textbox is scrolled to the bottom
textBox.AppendText(text);
textBox.SelectionStart = textBox.Text.Length;
textBox.ScrollToCaret();
}
}
Is there a neater way of purging lines of text from the top of the control that doesn't cause flickering? A textbox doesn't have the BeginUpdate()/EndUpdate() methods that a ListView has.
Is a TextBox control even the best suited control for a console log?
Edit: The TextBox flickering appears to be the textbox scrolling up to the top (while I purge the text at the top of the control), and then it immediately scrolls back down to the bottom. - it all happens very quickly, so I just see repeated flickering.
I've also just seen this question, and the suggestion was to use a ListBox, however I don't know if this will work in my situation, as (in most cases) I'm receiving the text for the ListBox one character at a time.
I find that using SelectedText = text will reduce the flicker dramatically. For very fast updates, the flicker will be localized to the new text only and you won't get any weird behavior from the scrollbar jumping around.
You can also use this to overwrite text written previously -- as you would need for updating a counter or download percentage for example:
Other than that, there doesn't seem to be any simple method for reducing flicker in the .NET TextBox.
Have you set double-buffering on your main window?
this code in your constructor after the InitializeComponent call will add double buffering and possibly reduce flicker.
this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer,true);
Did you try SuspendLayout() / ResumeLayout() around all your update operations?
You could also call Clear() on the textbox then reassign the truncated text.
If your try to implement some kind of log file viewer, you could use a ListBox instead.
The problem is that you are adding (removing) one character at a time repeatedly and quickly. One solution would be to buffer the characters as they are being added and update the textbox at greater intervals (regardless of the amount of characters), for example, every 250 milliseconds.
This would require:
Another option is to use both every 250 ms and 100 chars, whatever happens first. But this would probably complicate the code more without any tangible benefit.
Mathijs answer is works for me. I've modified it slightly so I can use with any control - a control extension:
So all you need to do is:
Works well. All flickering stops.
I found a solution looking on the internet: