Comment corriger le scintillement de contrôles Utilisateur

dans mon application je me déplace constamment d'un contrôle à l'autre. J'ai créé pas. des commandes de l'utilisateur, mais pendant la navigation Mes commandes deviennent vacillantes. il faut 1 ou 2 secondes pour mettre à jour. J'ai essayé de mettre ce

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.DoubleBuffer, true);

mais ça n'a pas aidé... Chaque contrôle a la même image de fond avec des contrôles différents. Quelle est donc la solution pour elle..

Merci.

96
demandé sur Royson 2010-04-10 10:24:17

12 réponses

ce n'est pas le genre de clignotement que le double-buffering peut résoudre. Ni BeginUpdate ni SuspendLayout. Vous avez trop de contrôles, le Backgrounddimage peut en faire un lot pire.

il commence quand L'UserControl se peint. Il dessine le fond, laissant des trous là où vont les fenêtres de contrôle de l'enfant. Chaque contrôle d'enfant reçoit alors un message pour se peindre, ils vont remplir le trou avec leur contenu de fenêtre. Quand vous avez beaucoup des contrôles, les trous sont visibles à l'utilisateur pour un certain temps. Ils sont normalement blancs, contrastant mal avec le fond lorsqu'il fait sombre. Ou ils peuvent être noirs si la forme a son Opacity ou TransparencyKey ensemble de propriété, contrastant mal avec à peu près n'importe quoi.

c'est une limitation assez fondamentale des formes de fenêtres, elle est collée avec la façon dont Windows rend windows. Corrigé par WPF btw, il n'utilise pas windows pour les contrôles enfants. Ce que vous voulez est double-buffering le formulaire entier, y compris les contrôles de l'enfant. C'est possible, vérifier mon code dans ce fil pour la solution. Il a des effets secondaires cependant, et n'augmente pas réellement la vitesse de peinture. Le code est simple, collez ceci dans votre forme (pas le contrôle de l'utilisateur):

protected override CreateParams CreateParams {
  get {
    CreateParams cp = base.CreateParams;
    cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    return cp;
  }
} 

il y a beaucoup de choses que vous pouvez faire pour améliorer la vitesse de peinture, au point que le scintillement n'est plus perceptible. Commencez par vous attaquer au fond D'écran. Ils peuvent être vraiment coûteux lorsque l'image source est grande et doit être rétrécie pour s'adapter à la commande. Changez la propriété Backgrounddimagelayout en "Tile". Si cela permet une accélération notable, revenez à votre programme de peinture et redimensionnez l'image pour être une meilleure correspondance avec la taille de contrôle typique. Ou écrire du code dans la méthode OnResize() de UC pour créer une copie de taille correcte de l'image afin qu'elle ne doive pas être redimensionnée chaque fois que le contrôle repeint. L'utilisation de la Format32bppPArgb format de pixel pour cette copie, il rend environ 10 fois plus rapide que n'importe quel autre format de pixel.

la prochaine chose que vous pouvez faire est d'empêcher les trous d'être si perceptible et contrastant mal avec l'image. Vous pouvez turn off le drapeau de style WS_CLIPCHILDREN pour L'UC, le drapeau qui empêche L'UC de peindre dans la zone où l'enfant contrôle aller. Coller ce code dans le code de UserControl:

protected override CreateParams CreateParams {
  get {
    var parms = base.CreateParams;
    parms.Style &= ~0x02000000;  // Turn off WS_CLIPCHILDREN
    return parms;
  }
}

les commandes de l'enfant vont maintenant se peindre sur l'image de fond. Vous pouvez encore les voir se peindre un par un, mais le trou blanc intermédiaire laid ou noir ne sera pas visible.

enfin et surtout, réduire le nombre de commandes enfants est toujours une bonne approche pour résoudre les problèmes de peinture lente. Outrepasser L'événement OnPaint() de L'UC et dessiner ce qui est maintenant affiché dans un enfant. Étiquette particulière et PictureBox sont très le gaspillage. Pratique pour pointer et cliquer mais leur alternative légère (dessiner une chaîne ou une image) ne prend qu'une seule ligne de code dans votre méthode OnPaint ().

276
répondu Hans Passant 2017-05-23 12:09:54

C'est un vrai problème, et la réponse de Hans Passant est grande pour sauver le clignotant. Cependant, il ya des effets secondaires comme il l'a mentionné, et ils peuvent être laids (UI laid). Comme indiqué, "Vous pouvez désactiver le drapeau de style WS_CLIPCHILDREN pour L'UC", mais cela ne l'éteint que pour un UC. Les composants sur le formulaire principal ont encore des problèmes.

exemple, une barre de défilement de panneau ne peint pas, parce qu'il est techniquement dans la zone de l'enfant. Toutefois, la composante enfant ne dessinez la barre de rouleau, de sorte qu'il ne soit pas peint jusqu'à ce que la souris sur (ou un autre événement le déclenche).

en outre, les icônes animées (changer les icônes dans une boucle d'attente) ne fonctionne pas. Supprimer des icônes sur un élément tabpage.ImageKey ne redimensionne pas et ne repeint pas les autres onglets de manière appropriée.

donc je cherchais un moyen d'éteindre les enfants WS_CLIPCHILDREN sur la peinture initiale de sorte que ma forme va charger bien peint, ou mieux encore ne l'allumer tout en redimensionnant ma forme avec beaucoup de composant.

le truc est de faire en sorte que L'application appelle CreateParams avec le style ws_ex_composited/WS_CLIPCHILDR? J'ai trouvé un piratage ici ( http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx ) et cela fonctionne très bien. Merci AngryHacker!

je mets L'appel TurnOnFormLevelDoubleBuffering() dans la forme ResizeBegin événement. TurnOffFormLevelDoubleBuffering() appelez L'événement ResizeEnd (ou laissez-le simplement WS_CLIPCHILDREN après qu'il ait été peint correctement.)

    int originalExStyle = -1;
    bool enableFormLevelDoubleBuffering = true;

    protected override CreateParams CreateParams
    {
        get
        {
            if (originalExStyle == -1)
                originalExStyle = base.CreateParams.ExStyle;

            CreateParams cp = base.CreateParams;
            if (enableFormLevelDoubleBuffering)
                cp.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED
            else
                cp.ExStyle = originalExStyle;

            return cp;
        }
    }

    public void TurnOffFormLevelDoubleBuffering()
    {
        enableFormLevelDoubleBuffering = false;
        this.MaximizeBox = true;
    }
8
répondu user2044810 2013-05-20 18:54:46

si vous faites n'importe quelle peinture personnalisée dans le contrôle (c.-à-d. en écrasant OnPaint) vous pouvez essayer le double tampon vous-même.

Image image;
protected override OnPaint(...) {
    if (image == null || needRepaint) {
        image = new Bitmap(Width, Height);
        using (Graphics g = Graphics.FromImage(image)) {
            // do any painting in image instead of control
        }
        needRepaint = false;
    }
    e.Graphics.DrawImage(image, 0, 0);
}

et invalider votre contrôle avec un bien NeedRepaint

sinon la réponse ci-dessus avec SuspendLayout et ResumeLayout est probablement ce que vous voulez.

4
répondu Patrick 2010-04-10 08:40:19

essayez les méthodes BeginUpdate/Endurpdate ou SuspendLayout/ResumeLayout. Voir la suite



clignotement lors de mises à jour de commandes dans WinForms (par ex. DataGridView)

3
répondu Brij 2017-05-23 12:25:54

sur le formulaire principal ou le contrôle de l'utilisateur où l'image d'arrière-plan réside définit la propriété BackgroundImageLayout à Center ou Stretch . Vous remarquerez une grande différence lorsque l'utilisateur le contrôle est rendu.

2
répondu revobtz 2014-05-31 16:00:39

j'ai essayé de l'ajouter comme commentaire, mais je n'ai pas assez de points. C'est la seule chose qui a jamais aidé mes problèmes de scintillement tellement merci à Hans pour son post. Pour tous ceux qui utilisent C++ builder comme moi voici la traduction

ajoutez la déclaration CreateParams au formulaire principal de votre demande .h de fichier par exemple

class TYourMainFrom : public TForm
{
protected:
    virtual void __fastcall CreateParams(TCreateParams &Params);
}

et ajoutez ceci à votre .fichier cpp

void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
    Params.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    TForm::CreateParams(Params);
}
2
répondu NoComprende 2016-09-05 09:10:15

mettez le code ci-dessous dans votre événement de constructeur ou OnLoad et si vous utilisez une sorte de contrôle d'utilisateur personnalisé qui a des sous-Contrôles, Vous aurez besoin de vous assurer que ces contrôles personnalisés sont également double tampon (même si dans la documentation de MS ils disent qu'il est réglé à true par défaut).

si vous faites un contrôle personnalisé, vous pourriez vouloir ajouter ce drapeau dans votre ctor:

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

vous pouvez utiliser ce code dans votre Formulaire / Commande:

foreach (Control control in Controls)
{
    typeof(Control).InvokeMember("DoubleBuffered",
        BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
        null, control, new object[] { true });
}

nous itérons tous les contrôles dans la forme/contrôle et l'accès à leur propriété DoubleBuffered et puis nous le changeons en true afin de rendre chaque contrôle sur la forme double tampon. La raison pour laquelle nous réfléchissons ici, c'est parce que imaginez que vous avez un contrôle qui a des contrôles d'enfants qui ne sont pas accessibles, de cette façon, même s'il s'agit de contrôles privés, nous changerons tout de même leur propriété pour vraie.

plus d'information à propos de la technique de double tampon peut être trouvé ici .

il y a une autre propriété que j'ai l'habitude de surcharger pour trier ce problème:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams parms = base.CreateParams;
        parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
        return parms;
    }
}

WS_EX_COMPOSITED - peint tous les descendants d'une fenêtre dans l'ordre de peinture de bas en haut en utilisant un double tampon.

vous pouvez trouver plus de ces drapeaux de style ici .

Espère que ça aide!

2
répondu Gilad Reich 2018-08-07 23:15:29

juste pour ajouter à la réponse Hans a donné:

(version TLDR: la transparence est plus lourde que vous le pensez, n'utilisez que des couleurs unies partout)

si WS_EX_COMPOSITED, DoubleBuffered et WS_CLIPCHILDREN n'ont pas résolu votre flicker( pour moi WS_CLIPCHILDREN l'a fait encore pire), essayez ceci: passez par tous vos contrôles et tout votre code, et partout où vous avez toute transparence ou semi-transparence pour BackColor, ForeColor, ou toute autre couleur, il suffit de supprimer c', utilisez uniquement des aplats de couleurs. Dans la plupart des cas où vous pensez que vous juste ont d'utiliser la transparence, vous ne le faites pas. Remodelez votre code et vos commandes, et utilisez des couleurs unies. J'ai eu de terribles, terribles clignotements et le programme était lent. Une fois que j'ai enlevé la transparence, elle a accéléré de manière significative, et il y a 0 clignotant.

EDIT: pour ajouter plus de détails, je viens de découvrir que WS_EX_COMPOSITED ne doit pas être à l'échelle de la fenêtre, il pourrait être appliqué juste pour contrôles spécifiques! Ce qui m'a sauvé beaucoup de mal. Il suffit de créer un contrôle personnalisé hérité de n'importe quel contrôle dont vous avez besoin, et de coller le contrôleur déjà posté pour WS_EX_COMPOSITED. De cette façon, vous obtenez un double-buffer de bas niveau sur ce contrôle seulement, en évitant les effets secondaires désagréables dans le reste de l'application!

1
répondu Daniel 2013-03-21 15:35:49

je sais que cette question est très ancienne, mais nous voulons donner mon expérience sur elle.

j'ai eu beaucoup de problèmes avec Tabcontrol clignotant dans une forme avec OnPaint et/ou OnPaintBackGround dans Windows 8 en utilisant .NET 4.0.

le seul pense que travaillé a été ne pas utiliser la méthode Graphics.DrawImage dans OnPaint l'emporte, en d'autres termes, quand le tirage a été fait directement aux graphiques fournis par le PaintEventArgs , même en peignant tout le rectangle, le scintillement disparut. Mais si vous appelez la méthode DrawImage , même en dessinant un Bitmap découpé, (créé pour un double buffer) le clignotant apparaît.

Espère que cela aide!

0
répondu ZeroWorks 2014-06-27 17:57:58

j'ai combiné ce scintillement fix et cette police fix , puis j'ai dû ajouter un peu de mon propre code pour démarrer une minuterie sur la peinture d'Invalider le TabControl quand il passe à l'écran et à l'arrière, etc..

tous les trois font ceci:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    private const int WM_PAINT = 0x0f;
    private const int WM_SETFONT = 0x30;
    private const int WM_FONTCHANGE = 0x1d;
    private System.Drawing.Bitmap buffer;
    private Timer timer = new Timer();
    public TabControlEx()
    {
        timer.Interval = 1;
        timer.Tick += timer_Tick;
        this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }
    void timer_Tick(object sender, EventArgs e)
    {
        this.Invalidate();
        this.Update();
        timer.Stop();
    }
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_PAINT) timer.Start();
        base.WndProc(ref m);
    }
    protected override void OnPaint(PaintEventArgs pevent)
    {
        this.SetStyle(ControlStyles.UserPaint, false);
        base.OnPaint(pevent);
        System.Drawing.Rectangle o = pevent.ClipRectangle;
        System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control);
        if (o.Width > 0 && o.Height > 0)
        DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height));
        pevent.Graphics.DrawImageUnscaled(buffer, 0, 0);
        this.SetStyle(ControlStyles.UserPaint, true);
    }

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        buffer = new System.Drawing.Bitmap(Width, Height);
    }
    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        this.OnFontChanged(EventArgs.Empty);
    }
    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);
        IntPtr hFont = this.Font.ToHfont();
        SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
        SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
        this.UpdateStyles();
    }
}

Je ne suis pas le créateur mais d'après ce que j'ai compris le bitmap fait tout le bug contournant.

C'était la seule chose qui définitivement résolu TabControl (avec icônes) flicker pour moi.

"151910920, la différence de résultat en vidéo: vanille tabcontrol vs tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

ps. vous aurez besoin de définir HotTrack = true, parce que cela corrige aussi ce bug

0
répondu user3732487 2016-03-24 04:42:36

avez-vous essayé Control.DoubleBuffered propriété?

Obtient ou définit une valeur indiquant si ce contrôle doit redessiner sa surface en utilisant un tampon secondaire pour réduire ou empêcher le scintillement.

Aussi ce et ce pourrait aider.

-2
répondu KMån 2010-04-10 10:06:59

il n'y a pas besoin de double tampon et tous ces trucs les gars...

une solution Simple...

si vous utilisez L'Interface MDI, il vous suffit de coller le code ci-dessous dans le formulaire principal. Il supprimera tout scintillement des pages. Cependant, certaines pages qui demandent plus de temps pour le chargement apparaîtront en 1 ou 2 secondes. Mais c'est mieux que de montrer une page clignotante dans laquelle chaque élément vient un par un.

C'est la meilleure solution pour l'ensemble de l'application. Voir le code à mettre dans la forme principale:

protected override CreateParams CreateParams {
  get {
    CreateParams cp = base.CreateParams;
    cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
    return cp;
  }
} 
-9
répondu Kshitiz 2012-10-02 20:41:37