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.
Mathijs answer is works for me. I've modified it slightly so I can use with any control - a control extension:
namespace System.Windows.Forms
{
public static class ControlExtensions
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
public static void Suspend(this Control control)
{
LockWindowUpdate(control.Handle);
}
public static void Resume(this Control control)
{
LockWindowUpdate(IntPtr.Zero);
}
}
}
So all you need to do is:
myTextBox.Suspend();
// do something here.
myTextBox.Resume();
Works well. All flickering stops.
I found a solution looking on the internet:
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
internal void FillTB(TextBox tb, string mes)
{
try
{
LockWindowUpdate(tb.Handle);
// Do your thingies with TextBox tb
}
finally
{
LockWindowUpdate(IntPtr.Zero);
}
}
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With