2009-07-15 8 views
6

J'ai un contrôle CheckedListBox (WinForms) (qui hérite de ListBox, googling montre que le problème est avec ListBox) qui est ancré aux quatre côtés de son formulaire. Lorsque le formulaire est redimensionné, le contrôle ListBox a un scintillement laid. J'ai essayé d'hériter CheckedListBox et de définir DoubleBuffered à true dans le ctor (cette technique fonctionne avec d'autres contrôles, y compris ListView et DataGridView), mais cela n'a eu aucun effet.Double buffered ListBox

J'ai essayé d'ajouter le style WS_EX_COMPOSITED à CreateParams, et cela a aidé, mais rend la forme redimensionnée plus lentement.

Y at-il un autre moyen d'éviter ce scintillement?

Répondre

10

J'avais des problèmes similaires avec une liste déroulante de propriétaires, mais ma solution était d'utiliser des objets BufferedGraphics. le propriétaire a dessiné, mais peut-être cela vous donnera-t-il un peu d'inspiration

J'ai trouvé que TextRenderer avait du mal à restituer l'emplacement correct à moins que je fournisse TextFormatFlags.Prese rveGraphicsTranslateTransform. L'alternative à ceci était d'utiliser P/Invoke pour appeler BitBlt pour copier directement les pixels entre les contextes graphiques. J'ai choisi cela comme le moindre de deux maux.

/// <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

Je viens de mettre en œuvre cela et cela fonctionne parfaitement. – test

+1

@Eric: D'où obtenez-vous le GDI? Est-ce une référence? Par exemple, j'ai essayé d'ajouter 'Graphics GDI = this.CreateGraphics();' mais il n'a pas la méthode CopyGraphics. Ou avez-vous d'abord importer Gdi32.dll? – Matt

+0

Ok - fonctionne maintenant. J'ai ajouté 'GDI32.dll' avec la méthode' BitBlt', l'ai enveloppé comme 'GDI.CopyGraphics (...)' et maintenant cela fonctionne ... la seule chose est que scintille exactement la même chose que la ListBox d'origine. Des idées comment résoudre ce problème? – Matt

2

Vous pouvez vérifier si le passage à un contrôle ListView avec des cases à cocher améliore les choses. Ce n'est pas aussi facile à gérer (mais bon, le WinForms ListBox n'est pas un coup de génie non plus), j'ai trouvé que son comportement de redimensionnement avec DoubleBuffered=true est supportable. Vous pouvez également essayer de réduire le scintillement en redéfinissant le dessin d'arrière-plan des formulaires parents, soit en fournissant un pinceau creux, soit en remplaçant WM_ERASEBKND en ne faisant rien et en renvoyant TRUE. (c'est correct si votre contrôle couvre la totalité de la zone client du formulaire parent, sinon vous auriez besoin d'une méthode de dessin en arrière-plan plus complexe.)

Je l'ai utilisé avec succès dans les applications Win32, mais je ne sais pas si le Le contrôle des formulaires ajoute une partie de sa propre magie qui rend ce non-fonctionnel

0

Cela a été traité en envoyant le message WM_SETREDRAW au contrôle.

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

Voir aussi: WM_SETREDRAW reference at MicrosoftLiaison Fixe

Si quelqu'un d'autre a utilisé des messages Windows sous .NET, s'il vous plaît mettre à jour cette annonce si nécessaire.

+0

Le flicker est causé par le redimensionnement, donc ce n'est pas 't une solution optimale. Cependant, je pourrais le faire quand même. – SLaks

+0

Cela ne résout pas le problème de scintillement sur ma ListBox. – AlainD

0

Bien que n'abordant pas le problème spécifique du scintillement, une méthode qui est fréquemment efficace pour ce type de problème consiste à mettre en cache un état minimal des éléments ListBox. Puis déterminez si vous avez besoin de redessiner le ListBox en effectuant un calcul sur chaque élément. Ne mettez à jour le ListBox que si au moins un élément doit être mis à jour (et bien sûr enregistrer ce nouvel état dans le cache pour le cycle suivant).