web-dev-qa-db-fra.com

Winforms: SuspendLayout/ResumeLayout n'est pas suffisant?

J'ai une bibliothèque de quelques "contrôles personnalisés". Nous avons essentiellement nos propres boutons, des panneaux d’angle arrondis et quelques boîtes de groupe avec une peinture personnalisée. Malgré les "maths" dans les méthodes OnPaint, les contrôles sont assez standard. La plupart du temps, nous ne faisons que tracer les coins arrondis et ajouter un dégradé à l'arrière-plan. Nous utilisons GDI + pour tout cela.

Ces contrôles sont corrects (et très jolis à nos yeux), cependant et malgré le DoubleBuffer, vous pouvez voir une redessin, surtout quand il y a 20 boutons ++ (par exemple) sur le même formulaire. Lors du chargement du formulaire, vous voyez les boutons dessiner… ce qui est agaçant.

Je suis presque sûr que nos boutons ne sont pas la solution la plus rapide au monde, mais ma question est la suivante: si le double tampon est "activé", tout ce rafraîchissement doit-il avoir lieu en arrière-plan et le sous-système Windows doit-il afficher les résultats "instantanément"?

D'autre part, s'il existe une boucle "complexe" foreach qui créera des étiquettes, ajoutez-les à un panneau (double tampon) et modifiez leurs propriétés, si nous suspendons la visualisation du panneau avant la boucle et si nous reprenons sa mise en page lorsque la boucle est en cours. dessus, toutes ces commandes (étiquettes et boutons) ne devraient-elles pas apparaître "presque instantanément"? Cela ne se produit pas comme ça, vous pouvez voir le panneau en cours de remplissage.

Une idée pourquoi cela ne se produit pas? Je sais que c'est difficile à évaluer sans exemple de code mais c'est difficile à reproduire aussi. Je pourrais faire une vidéo avec une caméra, mais croyez-moi sur celui-ci, ce n'est pas rapide :)

26

Nous avons vu ce problème aussi.

Une façon de résoudre ce problème consiste à suspendre complètement l’appel du contrôle jusqu’à ce que nous soyons prêts. 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

Vous devez notamment vérifier si vous avez défini BackColor = Transparent sur l’un des contrôles enfants de vos panneaux. BackColor = Transparent dégradera considérablement les performances de rendu, en particulier si les panneaux parents utilisent des dégradés.

Windows Forms n'utilise pas la transparence réelle, mais plutôt le "faux". Chaque contrôle Paint de l'appel enfant génère un appel Paint sur le parent afin que ce dernier puisse peindre son arrière-plan sur lequel le contrôle enfant peint son contenu afin qu'il apparaisse transparent.

Donc, si vous avez 50 contrôles enfants qui généreront 50 appels Paint supplémentaires sur le contrôle parent pour la peinture en arrière-plan. Et comme les gradients sont généralement plus lents, vous constaterez une dégradation des performances.

J'espère que cela t'aides.

Je vais aborder votre problème sous l'angle de la performance.

pour chaque boucle qui créera des étiquettes, les ajoutera à un panneau (double tampon) et changer leurs propriétés

Si tel est l'ordre, les choses sont faites, il y a place à amélioration. Commencez par créer toutes vos étiquettes, modifiez leurs propriétés et, quand elles sont toutes prêtes, ajoutez-les au panneau: Panel.Controls.AddRange(Control[])

La plupart du temps, nous ne faisons que dessiner Les coins arrondis et ajouter un dégradé À l'arrière-plan.

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

Mon conseil serait d'essayer de cacher certaines choses. Ouvrez Paint, tracez vos coins et enregistrez sur le disque ou générez une image en mémoire une seule fois. Puis chargez (et redimensionnez) au besoin. Même chose 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 Paint ou quoi que ce soit et, au moment de l'exécution, le charger et multiplier les valeurs Color par une autre couleur.

MODIFIER:

si nous suspendons l'affichage du panneau avant la boucle et reprenons la présentation du panneau lorsque la boucle est terminée

Ce n'est pas pour ça que SuspendLayout et ResumeLayout. Ils suspendent la logique de présentation, c'est-à-dire le positionnement automatique des contrôles. Les plus pertinentes avec FlowLayoutPanel et TableLayoutPanel.

En ce qui concerne le double tampon, je ne suis pas sûr que cela s'applique au code de tirage personnalisé (je n'ai pas essayé). Je suppose que vous devriez implémenter le vôtre. 

Double tampon en un mot: C'est très simple, quelques lignes de code. Sur l'événement Paint, effectuez un rendu dans un bitmap au lieu de rendre l'objet Graphics, puis tracez ce bitmap dans l'objet Graphics.

9
DonkeyMaster

En plus de la propriété DoubleBuffered, essayez également de l'ajouter au constructeur de votre contrôle:

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

Et si cela finit par ne pas suffire (ce que je vais dire, ce n'est pas le cas), jetez un œil à ma réponse à cette question et suspendez/reprenez le tirage au sort ou forme. Cela laisserait vos opérations de mise en page se terminer, puis tout le dessin une fois que c'est fait.

4
Adam Robinson

Vous voudrez peut-être regarder la réponse à ma question, Comment suspendre un tableau pour un contrôle et ses enfants? pour une meilleure suspension/reprise.

2
Simon

Cela ressemble à ce que vous recherchez est un affichage "composite", dans lequel l’application entière est dessinée en une seule fois, presque comme un gros bitmap. C'est ce qui se passe avec les applications WPF, à l'exception du "chrome" qui entoure l'application (des choses comme la barre de titre, les poignées de redimensionnement et les barres de défilement).

Notez que normalement, à moins que vous n'ayez manipulé certains des styles de fenêtre, chaque contrôle Windows Form est chargé de se peindre. C'est-à-dire que chaque contrôle obtient une fissure au niveau des messages WM_ Paint, WM_NCPAINT, WM_ERASEBKGND, etc., peignant les messages liés et les gère indépendamment. Cela signifie pour vous que la double mise en mémoire tampon ne s'applique qu'au contrôle unique auquel vous faites face. Pour vous rapprocher un peu de l'effet composite net, vous devez vous préoccuper non seulement des contrôles personnalisés que vous dessinez, mais également des contrôles de conteneur sur lesquels ils sont placés. Par exemple, si vous avez un formulaire contenant une GroupBox contenant à son tour un certain nombre de boutons dessinés personnalisés, chacun de ces contrôles doit avoir la propriété DoubleBuffered définie sur True. Notez que cette propriété est protégée. Cela signifie donc que vous héritez pour les différents contrôles (uniquement pour définir la propriété de double mise en mémoire tampon) ou que vous utilisez la réflexion pour définir la propriété protégée. En outre, tous les contrôles Windows Form ne respectent pas la propriété DoubleBuffered, car certains d'entre eux ne sont que des wrappers autour des contrôles "communs" natifs.

Il existe un moyen de définir un indicateur composé si vous ciblez Windows XP (et probablement plus tard). Il y a le style de fenêtre WS_ EX_ COMPOSITED. Je l'ai déjà utilisé pour mélanger les résultats. Cela ne fonctionne pas bien avec les applications hybrides WPF/WinForm et ne fonctionne pas bien avec le contrôle DataGridView. Si vous choisissez cette voie, veillez à effectuer de nombreux tests sur différentes machines car j'ai constaté des résultats étranges. En fin de compte, j'ai abandonné l'utilisation de cette approche.

2
Notre

Peut-être d'abord que vous utilisiez un tampon 'visible' (privé) réservé au contrôle, puis le restituiez:

Sous votre contrôle

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 de redimensionnement

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

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

2
gsgc

J'ai eu le même problème avec un tablelayoutpanel lors du changement de contrôles utilisateur que je voulais afficher.

Je me suis complètement débarrassé du scintillement en créant une classe qui a hérité de la table, puis activé le double tampon.

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
Kevin

J'ai eu beaucoup de problèmes similaires dans le passé et j'ai résolu le problème en utilisant une suite d'interface utilisateur tierce (c'est-à-dire DevExpress ) plutôt que les contrôles Microsoft standard. 

J'ai commencé par utiliser les contrôles standard de Microsoft, mais j'ai constaté que je débattais constamment des problèmes causés par leurs contrôles. Le problème est aggravé par le fait que Microsoft ne résout généralement aucun des problèmes identifiés et fait très peu pour fournir des solutions de contournement appropriées. 

Je suis passé à DevExpress et je n'ai que de bonnes choses à dire. Le produit est solide, ils fournissent une assistance et une documentation de qualité et, effectivement, ils écoutent leurs clients. Chaque fois que j'ai une question ou un problème, j'ai une réponse amicale dans les 24 heures. Dans quelques cas, j'ai trouvé un bogue et dans les deux cas, ils ont implémenté un correctif pour la prochaine version du service.

0
Dennis Ecclestone

J'ai vu de mauvais winforms vaciller sur des formulaires dont les contrôles faisaient référence à une police manquante.

Ce n'est probablement pas courant, mais cela vaut la peine de regarder si vous avez tout essayé.

0
jm.