2009-10-11 20 views
7

Mi aplicación WinForms tiene un TextBox que estoy usando como archivo de registro. Estoy añadiendo texto sin que el formulario parpadee usando TextBox.AppendText(string);, sin embargo, cuando intento purgar el texto anterior (dado que la propiedad .Text del control alcanza el límite .MaxLength), aparece un parpadeo horrible.Detener el parpadeo de TextBox durante la actualización

El código que estoy usando es el siguiente:

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

¿Hay una manera más ordenada de purgar las líneas de texto de la parte superior del control que no causa parpadeo? Un cuadro de texto no tiene los métodos BeginUpdate()/EndUpdate() que tiene un ListView.

¿Es un control TextBox incluso el control más adecuado para un registro de consola?

Editar: El parpadeo del cuadro de texto parece ser el cuadro de texto que se desplaza hacia arriba (mientras purgo el texto en la parte superior del control), y luego se desplaza de inmediato hacia abajo. - Todo sucede muy rápido, así que solo veo parpadeos repetidos.

También he visto this question, y la sugerencia era usar un ListBox, sin embargo, no sé si esto funcionará en mi situación, ya que (en la mayoría de los casos) estoy recibiendo el texto para el ListBox un personaje a la vez.

+1

Podría querer cambiar ese "si" por "while" - en caso de que eliminar la primera línea de texto no sea suficiente para permitir que el nuevo texto encaje en el TextBox. –

+0

Bien manchado Noam. Gracias. – Bryan

+2

Este post tiene algo más de información sobre esto - http://stackoverflow.com/questions/1333393/how-to-prevent-a-windows-forms-textbox-from-flickering-on-resize –

Respuesta

2

El problema es que está agregando (eliminando) un carácter a la vez de forma repetida y rápida. Una solución sería almacenar los caracteres conforme se agregan y actualizar el cuadro de texto a intervalos mayores (independientemente de la cantidad de caracteres), por ejemplo, cada 250 milisegundos.

Para ello sería necesario:

  • que tienen una matriz o pila de caracteres en el que se añaden
  • que tienen un temporizador que llamar a un delegado que realmente haría la actualización con los caracteres almacenados en el pila

Otra opción es usar tanto cada 250 ms como 100 caracteres, pase lo que pase primero. Pero esto probablemente complicaría más el código sin ningún beneficio tangible.

+0

¿Eso no solo aumentaría la tasa de parpadeo? – Bryan

+0

Eso me ha dado una idea que puedo usar hasta que encuentre una mejor solución. Cuando el control se llena, ahora purgo alrededor del 20% del contenido (puedo permitirme bajar un 20%, aunque prefiero que funcione correctamente). De esta forma, en lugar de ver el parpadeo constantemente cuando el control está lleno, es poco probable que lo notes, ya que el parpadeo solo puede ocurrir una vez por hora en lugar de varias veces por segundo. – Bryan

+0

Los períodos fueron sin mucha reflexión, debo admitir :) –

3

¿Ha establecido doble almacenamiento en la ventana principal?

este código en su constructor después de la llamada InitializeComponent agregará doble búfer y posiblemente reduzca el parpadeo.

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

+0

He intentado esto, desafortunadamente no hace ninguna diferencia. – Bryan

+0

Esto no ayudará; TextBox es inherentemente flickery y lo mejor que puedes esperar es reducirlo. http://stackoverflow.com/questions/1333393/how-to-prevent-a-windows-forms-textbox-from-flickering-on-resize –

2

¿Usted intentó SuspendLayout()/ResumeLayout() alrededor de todos sus operaciones de actualización?

También puede llamar a Borrar() en el cuadro de texto y luego reasignar el texto truncado.

Si intenta implementar algún tipo de visor de archivos de registro, podría utilizar un ListBox en su lugar.

+0

Intenté tanto el doble almacenamiento en búfer como SuspendLayout()/ResumeLayout(), desafortunadamente no parece hacer ninguna diferencia. He actualizado mi pregunta sobre el uso de un ListBox, no estoy seguro de que funcione debido al hecho de que (normalmente) agrego un carácter a la vez. – Bryan

+0

puede actualizar el último elemento en el cuadro de lista para agregar un carácter y si la línea está llena puede agregar un nuevo elemento de ListBox. – codymanix

+0

¿Incluyó * todas * sus operaciones en SuspendLayout()/ResumeLayout(), incluye la llamada ScrollToCaret? – codymanix

1

Me parece que el uso de SelectedText = texto reducirá dramáticamente el parpadeo. Para actualizaciones muy rápidas, el parpadeo se localizará solo en el nuevo texto y no obtendrá ningún comportamiento extraño de la barra de desplazamiento saltando.

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

También puede usar esto para sobrescribir el texto escrito previamente - a medida que se necesita para la actualización de un contador o descargar porcentaje por ejemplo:

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

Aparte de eso, hay no parece que ser un método simple para reducir el parpadeo en .NET TextBox.

10

he encontrado una solución buscando en 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); 
     } 
    } 
+0

gracias, también funciona para mí - de hecho usé la extensión de mkaj –

9

Mathijs respuesta es que funciona para mí. He modificado ligeramente para que pueda usar con cualquier control - una extensión de control:

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

    } 
} 

Así que todo lo que necesita hacer es:

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

funciona bien. Todo parpadeo se detiene.

+0

gracias, también funciona para mí –

+0

FYI, Esto no funciona bien si '// hago algo aquí. Es un evento de cambio de tamaño. – Dan

Cuestiones relacionadas