2009-07-15 14 views
6

Ho un controllo CheckedListBox (WinForms) (che eredita da ListBox; googling mostra che il problema è con ListBox) ancorato su tutti e quattro i lati del suo modulo. Quando il modulo viene ridimensionato, il ListBox presenta un brutto sfarfallio. Ho provato ad ereditare CheckedListBox e impostare DoubleBuffered a true nel Ctor (questa tecnica funziona con altri controlli, inclusi ListView e DataGridView), ma non ha avuto alcun effetto.ListBox Double Buffered

Ho provato ad aggiungere lo stile WS_EX_COMPOSITED a CreateParams e questo ha aiutato, ma rende la poltiglia di ridimensionamento del modulo più lentamente.

C'è qualche altro modo per evitare questo sfarfallio?

risposta

10

Avevo problemi simili anche se con una listbox disegnata dal proprietario La mia soluzione era utilizzare gli oggetti BufferedGraphics. Il tuo chilometraggio può variare con questa soluzione se il tuo elenco non è proprietario disegnato, ma forse vi darà qualche ispirazione

Ho trovato che TextRenderer ha avuto difficoltà a eseguire il rendering nella posizione corretta a meno che non ho supposto TextFormatFlags.Prese rveGraphicsTranslateTransform. L'alternativa a questo era usare P/Invoke per chiamare BitBlt per copiare direttamente i pixel tra i contesti grafici. Ho scelto questo come il minore dei due mali.

/// <summary> 
/// This class is a double-buffered ListBox for owner drawing. 
/// The double-buffering is accomplished by creating a custom, 
/// off-screen buffer during painting. 
/// </summary> 
public sealed class DoubleBufferedListBox : ListBox 
{ 
    #region Method Overrides 
    /// <summary> 
    /// Override OnTemplateListDrawItem to supply an off-screen buffer to event 
    /// handlers. 
    /// </summary> 
    protected override void OnDrawItem(DrawItemEventArgs e) 
    { 
     BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current; 

     Rectangle newBounds = new Rectangle(0, 0, e.Bounds.Width, e.Bounds.Height); 
     using (BufferedGraphics bufferedGraphics = currentContext.Allocate(e.Graphics, newBounds)) 
     { 
      DrawItemEventArgs newArgs = new DrawItemEventArgs(
       bufferedGraphics.Graphics, e.Font, newBounds, e.Index, e.State, e.ForeColor, e.BackColor); 

      // Supply the real OnTemplateListDrawItem with the off-screen graphics context 
      base.OnDrawItem(newArgs); 

      // Wrapper around BitBlt 
      GDI.CopyGraphics(e.Graphics, e.Bounds, bufferedGraphics.Graphics, new Point(0, 0)); 
     } 
    } 
    #endregion 
} 
+0

Ho appena implementato questo e funziona perfettamente. – test

+1

@Eric: Da dove viene GDI? È un riferimento? Ad esempio, ho provato ad aggiungere 'Graphics GDI = this.CreateGraphics();' ma non ha il metodo CopyGraphics. O hai importato Gdi32.dll in precedenza? – Matt

+0

Ok: funziona ora. Ho aggiunto 'GDI32.dll' con il metodo' BitBlt', l'ho avvolto come 'GDI.CopyGraphics (...)' e ora funziona ... l'unica cosa è che è lo sfarfallio proprio come il ListBox originale. Qualche idea su come aggiustarlo? – Matt

2

È possibile verificare se passare a un controllo ListView con caselle di controllo migliora la situazione. Non è così facile da gestire (ma hey, anche la ListBox di WinForms non è un colpo di genio), ho scoperto che il suo comportamento di ridimensionamento con DoubleBuffered=true è sopportabile.

In alternativa, si potrebbe tentare di ridurre lo sfarfallio sovrascrivendo il disegno sfondo forme controllanti - sia fornendo un pennello cavo, o preponderante WM_ERASEBKND facendo nulla e ritorno TRUE. (Va bene se il controllo copre l'intera area client del modulo padre, altrimenti sarebbe necessario un metodo di disegno di sfondo più complesso.

Ho usato questo con successo in applicazioni Win32, ma non so se il Il controllo dei moduli aggiunge un po 'della propria magia che rende questo non funzionale

0

utilizzato per inviare il messaggio WM_SETREDRAW al controllo.

const int WM_SETREDRAW = 0x0b; 

Message m = Message.Create(yourlistbox.Handle, WM_SETREDRAW, (IntPtr) 0, (IntPtr) 0); 
yourform.DefWndProc(ref m); 

// do your updating or whatever else causes the flicker 

Message m = Message.Create(yourlistbox.Handle, WM_SETREDRAW, (IntPtr) 1, (IntPtr) 0); 
yourform.DefWndProc(ref m); 

Consulta anche: WM_SETREDRAW reference at Microsoftcollegamento fisso

Se qualcun altro ha usato messaggi di Windows in .NET, si prega di aggiornare questo intervento, se necessario.

+0

Lo sfarfallio è causato dal ridimensionamento, quindi questo non è è una soluzione ottimale. Tuttavia, potrei farlo comunque. – SLaks

+0

Questo non risolve il problema di sfarfallio sul mio ListBox. – AlainD

0

Anche se non si affronta il problema specifico del flickering, un metodo che è spesso efficace per questo tipo di problema è memorizzare nella cache uno stato minimo degli elementi ListBox. Quindi determinare se è necessario ridisegnare il ListBox eseguendo alcuni calcoli su ciascun elemento. Aggiorna il ListBox solo se è necessario aggiornare almeno un elemento (e, naturalmente, salva questo nuovo stato nella cache per il ciclo successivo).