Остановка мерцания TextBox во время обновления - PullRequest
14 голосов
/ 11 октября 2009

В моем приложении WinForms есть TextBox, который я использую в качестве файла журнала. Я добавляю текст без мерцания формы, используя TextBox.AppendText(string);, однако, когда я пытаюсь удалить старый текст (поскольку свойство элемента управления .Text достигает предела .MaxLength), я получаю ужасное мерцание.

Код, который я использую, выглядит следующим образом:

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

Есть ли более аккуратный способ очистки строк текста сверху элемента управления, который не вызывает мерцания? У текстового поля нет методов BeginUpdate () / EndUpdate (), которые есть у ListView.

Является ли элемент управления TextBox лучшим элементом управления для журнала консоли?

Редактировать: Мерцание TextBox выглядит как текстовое поле, прокручиваемое вверх (пока я очищаю текст в верхней части элемента управления), а затем сразу же прокручивается обратно вниз. - все это происходит очень быстро, поэтому я просто вижу повторяющиеся мерцания.

Я также только что видел этот вопрос , и было предложено использовать ListBox, однако я не знаю, будет ли это работать в моей ситуации, так как (в большинстве случаев) я получение текста для ListBox по одному символу за раз.

Ответы [ 6 ]

12 голосов
/ 29 июля 2013

Mathijs ответ работает для меня. Я немного изменил его, поэтому могу использовать с любым элементом управления - расширением элемента управления:

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

    }
}

Так что все, что вам нужно сделать, это:

myTextBox.Suspend();
// do something here.
myTextBox.Resume();

Работает хорошо. Все мерцание прекращается.

11 голосов
/ 02 февраля 2012

Я нашел решение, глядя в Интернете:

    [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);
       }
    }
3 голосов
/ 11 октября 2009

Вы установили двойную буферизацию в главном окне?

этот код в вашем конструкторе после вызова InitializeComponent добавит двойную буферизацию и, возможно, уменьшит мерцание.

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

2 голосов
/ 02 ноября 2011

Я считаю, что использование SelectedText = text значительно уменьшит мерцание. Для очень быстрых обновлений мерцание будет локализовано только для нового текста, и вы не получите никакого странного поведения от прокручивающейся полосы прокрутки.

void UpdateTextBox(string message)
{
   myTextBox.SelectionStart = myTextBox.Text.Length;
   myTextBox.SelectedText = message;
}

Вы также можете использовать это для перезаписи текста, написанного ранее - как вам потребуется для обновления счетчика или процента загрузки, например:

void UpdateTextBox(string message, int jumpBack)
{
   myTextBox.SelectionStart = Math.Max(myTextBox.Text.Length - jumpBack, 0);
   myTextBox.SelectionLength = jumpBack;
   myTextBox.SelectedText = message;
}

Кроме этого, в TextBox .NET нет простого способа уменьшить мерцание.

2 голосов
/ 11 октября 2009

Проблема в том, что вы добавляете (удаляете) по одному символу за раз несколько раз и быстро. Одним из решений будет буферизация символов по мере их добавления и обновление текстового поля через большие интервалы (независимо от количества символов), например, каждые 250 миллисекунд.

Для этого потребуется:

  • чтобы иметь массив или стек символов, куда они добавляются
  • иметь таймер, который будет вызывать делегата, который фактически будет выполнять обновление с символами, хранящимися в стеке

Другой вариант - использовать каждые 250 мс и 100 символов, что бы ни случилось в первую очередь. Но это, вероятно, усложнит код без каких-либо ощутимых преимуществ.

2 голосов
/ 11 октября 2009

Пробовали ли вы SuspendLayout () / ResumeLayout () во всех операциях обновления?

Вы также можете вызвать Clear () для текстового поля, а затем переназначить усеченный текст.

Если вы попытаетесь реализовать какую-либо программу просмотра файлов журнала, вы можете использовать вместо нее ListBox.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...