2009-10-11 2 views
7

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

код я использую следующим образом:

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 выглядит как текстовое поле, прокручивающееся вверх (в то время как я очищаю текст в верхней части элемента управления), а затем он немедленно прокручивается вниз. - все происходит очень быстро, поэтому я просто вижу повторяющееся мерцание.

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

+1

Возможно, вы захотите изменить это «если» на «пока» - в случае, если удаление первой строки текста недостаточно, чтобы новый текст соответствовал текстовому блоку. –

+0

Хорошо пятнистый Ноам. Благодарю. – Bryan

+2

У этого сообщения есть дополнительная информация об этом - http://stackoverflow.com/questions/1333393/how-to-prevent-a-windows-forms-textbox-from-flickering-on-resize –

ответ

2

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

Это потребует:

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

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

+0

Это не просто увеличит скорость мерцания? – Bryan

+0

Это дало мне идею, хотя я могу использовать ее, пока не найду лучшее решение. Когда элемент управления заполняется, я теперь очищаю около 20% содержимого (я могу позволить себе падение на 20%, хотя я бы предпочел, чтобы он работал правильно). Таким образом, вместо того, чтобы постоянно мерцать, когда элемент управления заполнен, вы вряд ли заметите это, поскольку мерцание может происходить только один раз в час вместо нескольких раз в секунду. – Bryan

+0

Периоды были без большой мысли, я должен признать :) –

3

У вас установлена ​​двойная буферизация в главном окне?

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

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

+0

Я пробовал это, к сожалению, это не имеет никакого значения. – Bryan

+0

Это не поможет; TextBox по своей сути мерцает, и лучшее, на что вы можете надеяться, это уменьшить его. http://stackoverflow.com/questions/1333393/how-to-prevent-a-windows-forms-textbox-from-flickering-on-resize –

2

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

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

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

+0

Я попробовал как двойную буферизацию, так и SuspendLayout()/ResumeLayout(), к сожалению, они похоже, не имеет никакого значения. Я обновил свой вопрос об использовании ListBox, я не уверен, что он будет работать из-за того, что я (обычно) добавляю по одному символу за раз. – Bryan

+0

вы можете обновить последний элемент в списке, чтобы добавить символ, и если строка заполнена, вы можете добавить новый элемент ListBox. – codymanix

+0

Включили ли вы * все * ваши операции в SuspendLayout()/ResumeLayout(), включили вызов ScrollToCaret? – codymanix

1

Я нахожу, что использование 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; 
} 

Кроме этого, кажется, не там быть любым простым методом для уменьшения мерцания в .NET TextBox.

10

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

[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); 
     } 
    } 
+0

спасибо, работает для меня тоже - на самом деле я использовал расширение mkaj –

9

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

работает хорошо. Все мерцание останавливается.

+0

спасибо, тоже работает для меня –

+0

FYI. Это не работает, если '// делать что-то здесь. '- это событие изменения размера. – Dan