86 votes

Comment arrêter de clignoter C# winforms

J'ai un programme qui est essentiellement comme une application de peinture. Cependant, mon programme a des problèmes de scintillement. J'ai la ligne suivante dans mon code (qui devrait se débarrasser du scintillement - mais ne le fait pas) :

this.SetStyle(ControlStyles.AllPaintingInWmPaint 
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

mon code (moins les super et sous-classes pour les formes) est le suivant :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Paint
{
    public partial class Paint : Form
    {
        private Point startPoint;
        private Point endPoint;
        private Rectangle rect = new Rectangle();
        private Int32 brushThickness = 0;
        private Boolean drawSPaint = false;
        private List listOfShapes = new List();
        private Color currentColor;
        private Color currentBoarderColor;
        private Boolean IsShapeRectangle = false;
        private Boolean IsShapeCircle = false;
        private Boolean IsShapeLine = false;

        public SPaint()
        {

            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

            currentColor = Color.Red;
            currentBoarderColor = Color.DodgerBlue;
            IsShapeRectangle = true; 
        }

        private void panelArea_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = panelArea.CreateGraphics();

            if (drawSPaint == true)
            {

                Pen p = new Pen(Color.Blue);
                p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

                if (IsShapeRectangle == true)
                {
                    g.DrawRectangle(p, rect);
                }
                else if (IsShapeCircle == true)
                {
                    g.DrawEllipse(p, rect);
                }
                else if (IsShapeLine == true)
                {
                    g.DrawLine(p, startPoint, endPoint);
                }
            }
            foreach (Shapes shape in listOfShapes)
            {

                shape.Draw(g);

            }
        }

        private void panelArea_MouseDown(object sender, MouseEventArgs e)
        {

            startPoint.X = e.X;
            startPoint.Y = e.Y;

            drawSPaint = true;
        }

        private void panelArea_MouseMove(object sender, MouseEventArgs e)
        {

            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {

                if (e.X > startPoint.X)
                {
                    rect.X = startPoint.X;
                    rect.Width = e.X - startPoint.X;
                }
                else
                {
                    rect.X = e.X;
                    rect.Width = startPoint.X - e.X;
                }
                if (e.Y > startPoint.Y)
                {
                    rect.Y = startPoint.Y;
                    rect.Height = e.Y - startPoint.Y;
                }
                else
                {
                    rect.Y = e.Y;
                    rect.Height = startPoint.Y - e.Y;
                }

                panelArea.Invalidate();

            }

        }

        private void panelArea_MouseUp(object sender, MouseEventArgs e)
        {

            endPoint.X = e.X;
            endPoint.Y = e.Y;

            drawSPaint = false;

            if (rect.Width > 0 && rect.Height > 0)
            {
                if (IsShapeRectangle == true)
                {
                    listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
                }
                else if (IsShapeCircle == true)
                {
                    listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
                }
                else if (IsShapeLine == true)
                {
                    listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
                }

                panelArea.Invalidate();
            }
        }

        private void rectangleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeRectangle = true;
            IsShapeCircle = false;
            IsShapeLine = false; 
        }

        private void ellipseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeRectangle = false;
            IsShapeCircle = true;
            IsShapeLine = false; 
        }

        private void lineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeCircle = false;
            IsShapeRectangle = false;
            IsShapeLine = true; 
        }

        private void ThicknessLevel0_Click(object sender, EventArgs e)
        {
            brushThickness = 0; 
        }

        private void ThicknessLevel2_Click(object sender, EventArgs e)
        {
            brushThickness = 2; 
        }

        private void ThicknessLevel4_Click(object sender, EventArgs e)
        {
            brushThickness = 4; 
        }

        private void ThicknessLevel6_Click(object sender, EventArgs e)
        {
            brushThickness = 6; 
        }

        private void ThicknessLevel8_Click(object sender, EventArgs e)
        {
            brushThickness = 8; 
        }

        private void ThicknessLevel10_Click(object sender, EventArgs e)
        {
            brushThickness = 10; 
        }

        private void ThicknessLevel12_Click(object sender, EventArgs e)
        {
            brushThickness = 12; 
        }

        private void ThicknessLevel14_Click(object sender, EventArgs e)
        {
            brushThickness = 14; 
        }

        private void FillColour_Click(object sender, EventArgs e)
        {

            ColorDialog fillColourDialog = new ColorDialog();
            fillColourDialog.ShowDialog();
            currentColor = fillColourDialog.Color;
            panelArea.Invalidate(); 
        }

        private void button1_Click(object sender, EventArgs e)
        {

            ColorDialog fillColourDialog = new ColorDialog();
            fillColourDialog.ShowDialog();
            currentBoarderColor = fillColourDialog.Color;
            panelArea.Invalidate(); 
        }

    }
}

Comment puis-je arrêter le scintillement ?

*MISE À JOUR :*Ce code fonctionne en réalité très bien lorsque je dessine directement sur le formulaire. Cependant, lorsque j'essaie de dessiner sur le panneau, le scintillement devient un problème

9 votes

Avez-vous également défini this.DoubleBuffered = true; ?

1 votes

@ Marc Gravell j'ai juste essayé d'ajouter cette.DoubleBuffered = true; et ça clignote toujours comme jamais :S

0 votes

Est-ce que panelArea est plein de contrôles? L'invalidation fonctionne de manière récursive et pourrait donc inciter chaque contrôle enfant dans panelArea à se redessiner.

87voto

viper Points 125

Pour une "solution plus propre" et afin de continuer à utiliser le panneau de base, vous pourriez simplement utiliser la Réflexion pour implémenter le double buffering, en ajoutant ce code au formulaire qui contient les panneaux dans lesquels vous voulez dessiner :

    typeof(Panel).InvokeMember("DoubleBuffered", 
    BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, 
    null, DrawingPanel, new object[] { true });

Où "DrawingPanel" est le nom du panneau sur lequel vous voulez appliquer le double buffering.

Je sais qu'il s'est écoulé beaucoup de temps depuis que la question a été posée, mais cela pourrait aider quelqu'un à l'avenir.

4 votes

C'est une excellente solution! Très simple à mettre en œuvre et fonctionne exactement comme désiré.

6 votes

@musefan Je suis tellement content de t'avoir aidé :D Je suis content qu'une solution publiée 2 ans après la question originale puisse encore aider les gens ! C'est pourquoi stack overflow est si bon! :)

1 votes

Il clignote toujours pour moi. Où dois-je ajouter ce code? PS. dans cette réponse, il devrait être dit "vous avez besoin de 'using System.Reflection' en haut".

69voto

BlueMonster Points 1692

Enfin résolu le scintillement. Comme je dessinais sur un panneau au lieu du formulaire, la ligne de code ci-dessous ne résoudra pas le scintillement :

this.SetStyle(
    ControlStyles.AllPaintingInWmPaint | 
    ControlStyles.UserPaint | 
    ControlStyles.DoubleBuffer, 
    true);

SetStyle doit être de type 'YourProject.YourProject' (ou en être dérivé) donc, vous devez créer une classe comme celle-ci (de sorte que vous puissiez utiliser MyPanel qui sera dérivé de SPaint.SPaint et donc vous permettant d'utiliser double buffering directement pour le panneau - plutôt que pour le formulaire) :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPaint; 

namespace YourProject
{
    public class MyPanel : System.Windows.Forms.Panel
    {
        public MyPanel()
        {
            this.SetStyle(
                System.Windows.Forms.ControlStyles.UserPaint | 
                System.Windows.Forms.ControlStyles.AllPaintingInWmPaint | 
                System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, 
                true);
        }
    }
}

Après avoir fait cela (bien que vous ne devriez vraiment jamais modifier le code du designer à moins de vraiment savoir ce que vous faites), vous devrez éditer le Form.Designer.cs. À l'intérieur de ce fichier, vous trouverez du code qui ressemble à ceci :

this.panelArea = new YourProject.MyPanel();

La ligne ci-dessus doit être modifiée en :

this.panelArea = new MyPanel(); 

Après avoir complété ces étapes, mon programme de dessin ne scintille plus.

Pour quiconque rencontrant le même problème, le problème est enfin résolu.

Profitez-en !

0 votes

Mais cela n'apparaît plus dans le designer. Comment puis-je le réparer?

0 votes

@WingerSendon - pour éviter les problèmes de conception, utilisez une solution "code", telle que viper's, qui définit le double-buffering à l'exécution.

61voto

user3641393 Points 11

Copiez et collez ceci dans votre projet

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

Cela active le double buffering pour tous les contrôles du niveau du formulaire vers le bas, sinon le double buffering doit être activé individuellement pour chacun... vous voudrez peut-être affiner le double buffering après cela car le double buffering général peut entraîner des effets secondaires indésirables.

0 votes

C'est la seule solution qui fonctionne réellement.

0 votes

Cela a fonctionné parfaitement dans mon cas. Application Winforms sur .NET 6.

16voto

ohmusama Points 2365

J'ai eu le même problème. Je n'ai jamais pu me débarrasser à 100% du scintillement (voir point 2), mais j'ai utilisé ceci

protected override void OnPaint(PaintEventArgs e) {}

aussi bien que

this.DoubleBuffered = true;

Le principal problème du scintillement est de s'assurer que vous

  1. peignez les choses dans le bon ordre!
  2. assurez-vous que votre fonction de dessin est < d'environ 1/60e de seconde

winforms invoque la méthode OnPaint chaque fois que le formulaire doit être redessiné. Il existe de nombreuses façons de le désactiver, y compris le déplacement du curseur de la souris sur le formulaire peut parfois déclencher un événement de redessin.

Une note importante à propos de OnPaint, c'est que vous ne recommencez pas à zéro à chaque fois, mais vous recommencez à l'endroit où vous étiez, si vous remplissez l'arrière-plan en couleur, vous risquez de provoquer un scintillement.

Enfin, votre objet gfx. À l'intérieur de OnPaint, vous devrez recréer l'objet graphique, mais SEULEMENT si la taille de l'écran a changé. Recréer l'objet est très coûteux, et il doit être supprimé avant d'être recréé (la collecte des ordures ne le gère pas correctement à 100% ou c'est ce que dit la documentation). J'ai créé une variable de classe

protected Graphics gfx = null;

et l'ai utilisée localement dans OnPaint comme ceci, mais c'était parce que j'avais besoin d'utiliser l'objet gfx à d'autres endroits dans ma classe. Sinon, NE FAITES PAS CECI. Si vous ne faites de la peinture que dans OnPaint, veuillez utiliser e.Graphics !!

// nettoyer l'ancien objet graphique
gfx.Dispose();

// recréer l'objet graphique (ne pas utiliser e.Graphics, car nous devons l'utiliser
// dans d'autres fonctions)
gfx = this.CreateGraphics();

J'espère que cela vous aidera.

0 votes

Cela n'a pas résolu mon problème :( .. ce code fonctionne très bien lorsque je dessine directement sur le formulaire. Cependant, lorsque j'essaie de dessiner sur le panneau, le scintillement devient un problème...

0 votes

@BlueMonster puis essayez Panel.DoubleBuffered = true;

0 votes

Je recommande de créer un objet enfant de la classe panel, héritant de panel, puis de remplacer la fonction protected override void OnPaint(PaintEventArgs e) {} de cet enfant. Le nouvel objet apparaîtra dans la liste des éléments à déposer dans votre formulaire. Vous pouvez également modifier manuellement le code pour changer le type de référence, si vous avez déjà créé le panel et n'avez pas la possibilité simple de le recréer.

4voto

Tom Points 374

Le double buffering ne va pas beaucoup aider ici, je crains. J'ai rencontré ce problème il y a un moment et j'ai fini par ajouter un panneau séparé d'une manière plutôt maladroite mais cela a fonctionné pour mon application.

Rendre le panneau original que vous avez (panelArea) une zone transparente, et le placer au-dessus d'un 2ème panneau, que vous appelez par exemple panelDraw. Assurez-vous que panelArea est en avant. J'ai fait cela rapidement et cela a éliminé le scintillement, mais la forme qui était dessinée était étalée donc ce n'est pas une solution complète non plus.

Un panneau transparent peut être créé en remplaçant certaines actions de peinture du panneau original :

public class ClearPanel : Panel
{
    public ClearPanel(){}

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams createParams = base.CreateParams;
            createParams.ExStyle |= 0x00000020;
            return createParams;
        }
    }

    protected override void OnPaintBackground(PaintEventArgs e){}
}

L'idée est de gérer le dessin de la forme temporaire pendant l'événement MouseMove du 'panelArea' et de REDISSINEZ SEULEMENT le 'panelDraw' lors de l'événement MouseUp.

// Utilisez l'événement de peinture du panelDraw pour dessiner les formes terminées
void panelDraw_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelDraw.CreateGraphics();

    foreach (Rectangle shape in listOfShapes)
    {
        shape.Draw(g);
    }
}

// Utilisez l'événement de peinture du panelArea pour mettre à jour la nouvelle forme glissante...
private void panelArea_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelArea.CreateGraphics();

    if (dessinSETPaint == true)
    {
        Pen p = new Pen(Color.Blue);
        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

        if (IsShapeRectangle == true)
        {
            g.DrawRectangle(p, rect);
        }
        else if (IsShapeCircle == true)
        {
            g.DrawEllipse(p, rect);
        }
        else if (IsShapeLine == true)
        {
            g.DrawLine(p, startPoint, endPoint);
        }
    }
}

private void panelArea_MouseUp(object sender, MouseEventArgs e)
{

    endPoint.X = e.X;
    endPoint.Y = e.Y;

    dessinSETPaint = false;

    if (rect.Width > 0 && rect.Height > 0)
    {
        if (IsShapeRectangle == true)
        {
            listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
        }
        else if (IsShapeCircle == true)
        {
            listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
        }
        else if (IsShapeLine == true)
        {
            listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
        }

        panelArea.Invalidate();
    }

    panelDraw.Invalidate();
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X