Winforms: SuspendLayout / ResumeLayout n'est pas suffisant?

j'ai une bibliothèque de quelques "commandes personnalisées". Essentiellement, nous avons nos propres boutons, panneaux de coin rond, et quelques boîtes de groupe avec un peu de peinture sur mesure. Malgré les" maths " dans les méthodes OnPaint, les contrôles sont assez standard. La plupart du temps, tout ce que nous faisons est de dessiner les coins arrondis et ajouter la pente à l'arrière-plan. Nous utilisons GDI+ pour tout cela.

ces contrôles sont ok (et très belle apparence selon nos clients), cependant et malgré le DoubleBuffer, vous pouvez voir quelques modifications, surtout quand il y a 20 boutons++ (par exemple) sur le même formulaire. Sur la charge de forme vous voyez les boutons de dessin... qui est ennuyeux.

je suis assez sûr que nos boutons ne sont pas la chose la plus rapide sur terre, mais ma question Est: Si double tampon est" on", ne devrait pas tout ce redraw se produire en arrière-plan et le sous-système Windows devrait montrer les résultats" instantanément"?

par contre, s'il y a une boucle" complexe" va créer des étiquettes, les ajouter à un panneau (double tampon) et changer leurs propriétés, si nous suspendlayout du panneau avant la boucle et reprendre la disposition du panneau lorsque la boucle est terminée, tous ces contrôles (étiquettes et boutons) ne devraient-ils pas apparaître "presque instantanément"? Cela ne se produit pas comme cela, vous pouvez voir le panneau étant rempli.

savez-vous pourquoi cela n'arrive pas? Je sais que c'est difficile à évaluer sans code échantillon mais c'est difficile à répliquer aussi. Je pourrais faire un la vidéo avec un appareil photo, mais croyez-moi sur ce point, il n'est pas rapide :)

26
demandé sur Martin Marconcini 2009-05-07 18:47:29

10 réponses

Nous avons vu ce problème aussi.

une façon que nous avons vu pour" réparer " il est de suspendre complètement le dessin de la commande jusqu'à ce que nous sommes prêts à aller. Pour ce faire, nous envoyons le message WM_SETREDRAW au contrôle:

// Note that WM_SetRedraw = 0XB

// Suspend drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);

...

// Resume drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
12
répondu Judah Himango 2009-05-07 15:03:31

une des choses que vous devriez regarder est de savoir si vous avez mis BackColor=Transparent sur l'un des contrôles enfant de vos panneaux. Le BackColor = Transparent dégradera de manière significative les performances de rendu, surtout si les panneaux parents utilisent des gradients.

Windows Forms n'utilise pas la transparence réelle, il est plutôt utilise "faux" un. Chaque appel de peinture de contrôle d'enfant génère appel de peinture sur le parent de sorte que le parent peut peindre son arrière-plan sur lequel l'enfant contrôle peint son contenu de sorte qu'il semble transparent.

donc si vous avez 50 commandes enfant qui généreront 50 appels de peinture supplémentaires sur le contrôle parent pour la peinture de fond. Et puisque les gradients sont généralement plus lents, vous verrez une dégradation des performances.

Espérons que cette aide.

11
répondu DevComponents - Denis Basaric 2009-05-18 18:30:16

j'aborderai votre problème sous l'angle de la performance.

pourchaque boucle qui va créer des étiquettes, ajoutez-les à un panneau (double tampon) et de changer leurs propriétés

si c'est l'ordre que les choses sont faites, il y a place à amélioration. Créez d'abord toutes vos étiquettes, changez leurs propriétés, et quand elles sont toutes prêtes, ajoutez-les au panneau: Panel.Controls.AddRange(Control[])

la plupart des le temps, tout ce que nous faisons est le tirage au sort les coins arrondis et ajouter dégradé à l'arrière-plan

faites-vous la même chose encore et encore? Comment sont vos dégradés généré? Écrire une image ne peut pas être si lent. Une fois j'ai dû créer un gradient 1680x1050 en mémoire, et c'était vraiment rapide, comme, trop rapide pour Stopwatch , donc dessiner un gradient ne peut pas être si dur.

mon conseil serait d'essayer de cacher des choses. Ouvrez la peinture, dessinez votre corners et enregistrer sur le disque, ou générer une image en mémoire juste une fois. Puis chargez (et redimensionnez) au besoin. De même pour le dégradé.

même si différents boutons ont des couleurs différentes, mais le même motif, vous pouvez créer un bitmap avec de la peinture ou n'importe quoi et à l'exécution chargez-le et multipliez les valeurs de couleur par une autre couleur.

EDIT:

si nous suspendons l'audience devant le boucle et reprendre la disposition du panneau lorsque la boucle est terminée

ce N'est pas à ça que servent SuspendLayout et ResumeLayout. Ils suspendre la logique de présentation, qui est, le positionnement automatique des contrôles. La plus pertinente avec FlowLayoutPanel et TableLayoutPanel.

en ce qui concerne le doublebuffering, Je ne suis pas sûr qu'il s'applique au code de tirage personnalisé (n'ont pas essayé). Je suppose que vous devriez mettre en œuvre la vôtre.

dans le Doublebuffering en bref: C'est très simple, quelques lignes de code. Sur l'événement paint, faites un rendu à un bitmap au lieu de faire un rendu à l'objet Graphics , puis dessinez ce bitmap à l'objet Graphics .

8
répondu DonkeyMaster 2009-05-07 16:57:31

en plus de la propriété DoubleBuffered , essayez aussi d'ajouter ceci au constructeur de votre contrôle:

SetStyle(ControlStyles.OptimizedDoubleBuffer | 
         ControlStyles.AllPaintingInWmPaint, true);

et si cela finit par ne pas être suffisant (ce que je vais prendre des risques et dire que ce n'est pas le cas), envisager d'avoir un regard sur ma réponse à cette question et suspendre/reprendre le remaniement du panneau ou de la forme. Cela permettrait à vos opérations de mise en page complète, puis faire tout le dessin une fois que c'est fait.

4
répondu Adam Robinson 2017-05-23 12:17:20

il semble que ce que vous recherchez est un écran" composé", où l'application entière est dessinée en une seule fois, presque comme un grand bitmap. C'est ce qui se passe avec les applications WPF, à l'exception du "chrome" entourant l'application (des choses comme la barre de titre, redimensionnez les poignées et les barres de défilement).

notez que normalement, sauf si vous avez modifié certains des styles de fenêtres, chaque contrôle de forme de fenêtres est responsable de la peinture elle-même. Qui est, à chaque contrôle obtient une fissure à la WM_ PAINT, WM_ NCPAINT, WM_ERASEBKGND, etc peinture des messages liés et traite ces messages indépendamment. Ce que cela signifie pour vous, c'est que le double buffering ne s'applique qu'à la commande unique à laquelle vous avez affaire. Pour vous rapprocher d'un effet propre et composté, vous devez vous préoccuper non seulement de vos contrôles personnalisés que vous dessinez, mais aussi des contrôles de conteneurs sur lesquels ils sont placés. Par exemple, si vous avez un formulaire qui contient une à son tour contient un certain nombre de boutons dessinés sur mesure, chacun de ces contrôles devrait avoir là la propriété DoubleBuffered réglée à True. Notez que cette propriété est protégée, donc cela signifie que soit vous finissez par hériter pour les différents contrôles (juste pour définir la double propriété tampon) ou vous utilisez la réflexion pour définir la propriété protégée. En outre, tous les contrôles de forme de fenêtres ne respectent pas la propriété DoubleBuffered, car à l'intérieur certains D'entre eux sont juste enveloppants autour des contrôles "communs" natifs.

il y a un moyen de mettre un drapeau composé Si vous ciblez Windows XP (et probablement plus tard). Il y a le style de fenêtre compostée WS_ EX_. Je l'ai déjà utilisé pour mélanger les résultats. Il ne fonctionne pas bien avec les applications hybrides WPF/WinForm et ne fonctionne pas bien avec le contrôle DataGridView. Si vous allez dans cette voie, soyez sûr que vous faites beaucoup de tests sur différentes machines parce que j'ai vu des résultats étranges. En fin de compte, j'ai abandonné l'utilisé de cette approche.

2
répondu 2009-08-17 21:26:42

Vous pouvez voir la réponse à ma question, Comment puis-je suspendre la peinture pour un contrôle et ses enfants? pour une meilleure suspension/reprise.

2
répondu Simon 2017-05-23 12:17:20

peut-être d'abord utiliser un tampon "visible" (privé) de contrôle et ensuite le rendre:

Dans votre panneau d'

BufferedGraphicsContext gfxManager;
BufferedGraphics gfxBuffer;
Graphics gfx;

une fonction pour installer des graphiques

private void InstallGFX(bool forceInstall)
{
    if (forceInstall || gfxManager == null)
    {
        gfxManager = BufferedGraphicsManager.Current;
        gfxBuffer = gfxManager.Allocate(this.CreateGraphics(), new Rectangle(0, 0, Width, Height));
        gfx = gfxBuffer.Graphics;
    }
}

dans sa méthode de peinture

protected override void OnPaint(PaintEventArgs e)
{
    InstallGFX(false);
    // .. use GFX to draw
    gfxBuffer.Render(e.Graphics);
}

dans sa méthode resize

protected override void OnSizeChanged(EventArgs e)
{
    base.OnSizeChanged(e);
    InstallGFX(true); // To reallocate drawing space of new size
}

le code ci-dessus a été quelque peu testé.

2
répondu Peter Mortensen 2012-12-24 17:15:01

j'ai eu le même problème avec un tablelayoutpanel lors de la commutation usercontrols que je voulais affiché.

je me suis complètement débarrassé du clignotant en créant une classe qui hérita de la table, puis permit le doublebuffering.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace myNameSpace.Forms.UserControls
{
    public class TableLayoutPanelNoFlicker : TableLayoutPanel
    {
        public TableLayoutPanelNoFlicker()
        {
            this.DoubleBuffered = true;
        }
    }
}
0
répondu Kevin 2010-02-04 19:23:03

j'ai eu beaucoup de problèmes similaires dans le passé, et la façon dont je l'ai résolu était d'utiliser une suite D'interface utilisateur tiers (c'est-à-dire, DevExpress ) plutôt que les commandes Microsoft standard.

j'ai commencé à utiliser les commandes standard de Microsoft, mais j'ai constaté que j'étais constamment en train de déboguer les problèmes qui étaient causés par leurs commandes. Le problème est aggravé par le fait que Microsoft ne règle généralement pas les problèmes identifiés et ils font très peu pour fournir des solutions de rechange appropriées.

Je suis passé à DevExpress, et je n'ai que de bonnes choses à dire. Le produit est solide, ils fournissent un grand soutien et de la documentation et oui, ils écoutent réellement leurs clients. Chaque fois que j'ai eu une question ou un problème, j'ai eu une réponse amicale dans les 24 heures. Dans quelques cas, j'ai trouvé un bug et dans les deux cas, ils ont mis en place un correctif pour la prochaine version du service.

0
répondu Dennis Ecclestone 2012-12-24 17:11:40

j'ai vu de mauvais winforms clignoter sur des formulaires où les contrôles faisaient référence à une police manquante.

ce n'est probablement pas commun, mais ça vaut la peine de regarder si vous avez essayé tout le reste.

0
répondu jm. 2016-01-24 04:14:10