Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stopping TextBox flicker during update

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.

like image 270
Bryan Avatar asked Oct 11 '09 09:10

Bryan


3 Answers

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.

like image 182
mkaj Avatar answered Nov 11 '22 22:11

mkaj


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);
       }
    }
like image 20
Mathijs Beentjes Avatar answered Nov 11 '22 21:11

Mathijs Beentjes


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

like image 4
Alastair Pitts Avatar answered Nov 11 '22 21:11

Alastair Pitts