2009-10-11 5 views
7

L'applicazione WinForms dispone di un controllo TextBox che sto utilizzando come file di registro. Sto aggiungendo il testo senza lo sfarfallio del modulo utilizzando TextBox.AppendText(string);, tuttavia quando provo a eliminare il vecchio testo (poiché la proprietà .Text del controllo raggiunge il limite .MaxLength), ottengo uno sfarfallio tremendo.Arresto dello sfarfallio TextBox durante l'aggiornamento

Il codice che sto utilizzando è il seguente:

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

Esiste un modo più ordinato di spurgo righe di testo dalla parte superiore del controllo che non causa lo sfarfallio? Una casella di testo non ha i metodi BeginUpdate()/EndUpdate() che ha un ListView.

Un controllo TextBox è persino il controllo più adatto per un log di console?

Modifica: Lo sfarfallio della casella di testo sembra essere la casella di testo che scorre verso l'alto (mentre rimuovo il testo nella parte superiore del controllo), quindi scorre immediatamente verso il basso. - Succede tutto molto velocemente, quindi vedo solo lo sfarfallio ripetuto.

Ho anche appena visto this question, e il suggerimento era di usare un ListBox, tuttavia non so se questo funzionerà nella mia situazione, come (nella maggior parte dei casi) sto ricevendo il testo per il ListBox un personaggio alla volta

+1

Potrebbe voler cambiare "se" in "while", nel caso in cui l'eliminazione della prima riga di testo non sia sufficiente per consentire al nuovo testo di adattarsi al TextBox. –

+0

Notam ben chiazzato. Grazie. – Bryan

+2

Questo post ha qualche informazione in più su questo - http://stackoverflow.com/questions/1333393/how-to-prevent-a-windows-forms-textbox-from-flickering-on-resize –

risposta

2

Il problema è che si aggiunge (rimuovendo) un carattere alla volta ripetutamente e rapidamente. Una soluzione sarebbe quella di bufferizzare i caratteri mentre vengono aggiunti e aggiornare la casella di testo a intervalli maggiori (indipendentemente dalla quantità di caratteri), ad esempio, ogni 250 millisecondi.

Ciò richiederebbe:

  • di avere una matrice o pila di caratteri dove ottengono aggiunti
  • di avere un timer che chiamare un delegato che effettivamente fare l'aggiornamento con i caratteri memorizzati nel stack

Un'altra opzione consiste nell'utilizzare sia ogni 250 ms sia 100 caratteri, qualunque cosa accada prima. Ma questo probabilmente complicherebbe ulteriormente il codice senza alcun beneficio tangibile.

+0

Questo non solo aumenterebbe il tasso di sfarfallio? – Bryan

+0

Mi è stata data un'idea che posso usare fino a quando non trovo una soluzione migliore. Quando il controllo si riempie, ora elimino circa il 20% del contenuto (posso permettermi di perdere il 20%, anche se preferirei farlo funzionare correttamente). In questo modo, invece di vedere lo sfarfallio costantemente quando il controllo è pieno, è improbabile che lo si noti poiché lo sfarfallio potrebbe verificarsi solo una volta all'ora anziché diverse volte al secondo. – Bryan

+0

I periodi erano senza molto pensiero, devo ammettere :) –

3

Hai impostato il doppio buffer nella finestra principale?

questo codice nel costruttore dopo la chiamata InitializeComponent aggiungerà doppio buffering e probabilmente ridurrà lo sfarfallio.

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

+0

Ho provato questo, purtroppo non fa alcuna differenza. – Bryan

+0

Questo non aiuterà; TextBox è intrinsecamente sfarfallio e il meglio che puoi sperare è ridurlo. http://stackoverflow.com/questions/1333393/how-to-prevent-a-windows-forms-textbox-from-flickering-on-resize –

2

Hai provato SuspendLayout()/ResumeLayout() intorno a tutti i tuoi operazioni di aggiornamento?

È anche possibile chiamare Clear() sulla casella di testo, quindi riassegnare il testo troncato.

Se si tenta di implementare qualche tipo di visualizzatore di file di registro, è possibile utilizzare invece un ListBox.

+0

Ho provato sia il doppio buffering che SuspendLayout()/ResumeLayout(), sfortunatamente non sembrano fare alcuna differenza Ho aggiornato la mia domanda sull'utilizzo di un ListBox, non sono sicuro che funzionerà a causa del fatto che (di solito) aggiungo un carattere alla volta. – Bryan

+0

è possibile aggiornare l'ultimo elemento nella casella di riepilogo per aggiungere un carattere e se la linea è piena è possibile aggiungere un nuovo elemento ListBox. – codymanix

+0

Hai incluso * tutto * delle tue operazioni in SuspendLayout()/ResumeLayout(), includi la chiamata ScrollToCaret? – codymanix

1

Trovo che l'uso di SelectedText = testo ridurrà drasticamente lo sfarfallio. Per aggiornamenti molto veloci, lo sfarfallio sarà localizzato solo nel nuovo testo e non si otterrà alcun comportamento strano dalla barra di scorrimento.

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

È inoltre possibile utilizzare questo per sovrascrivere il testo scritto in precedenza - come si avrebbe bisogno per l'aggiornamento di un contatore o scaricare percentuale ad esempio:

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

Oltre a questo, ci non sembra essere un metodo semplice per ridurre lo sfarfallio in .NET TextBox.

10

ho trovato una soluzione cercando su 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

grazie, funziona anche per me - infatti ho usato l'estensione di mkaj –

9

Mathijs risposta è funziona per me. Ho modificato leggermente in modo da poter utilizzare con qualsiasi controllo - un'estensione di controllo:

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

    } 
} 

Quindi, tutto quello che dovete fare è:

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

funziona bene. Tutti gli sfarfallio si fermano.

+0

grazie, funziona anche per me –

+0

FYI, Questo non funziona bene se '// fai qualcosa qui. 'È un evento di ridimensionamento. – Dan