comment arrêter le scintillement c # winforms

J'ai un programme qui ressemble essentiellement à une application de peinture. Cependant, mon programme a quelques 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<Shapes> listOfShapes = new List<Shapes>();
        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 clignotement?

*UPDATE: *ce code fonctionne très bien lorsque je dessine directement sur le formulaire. Cependant, quand j'essaie de dessiner sur le panneau, le scintillement devient un problème

56
demandé sur BigBug 2011-11-08 10:03:19

13 réponses

Pour une "solution plus propre" et afin de continuer à utiliser le Panneau de base, vous pouvez simplement utiliser Reflection pour implémenter la double mise en mémoire tampon, en ajoutant ce code à la forme 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 que vous voulez faire la double mise en mémoire tampon.

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

51
répondu viper 2013-04-04 15:19:05

Enfin résolu le scintillement. Puisque 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 dérivé de celui-ci) par conséquent, vous devez créer une classe en tant que telle (de sorte que vous pouvez utiliser MyPanel qui sera dérivé de SPaint.SPaint et donc vous permettant d'utiliser doublebuffering directement pour le panneau-plutôt que 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 devrait vraiment jamais éditer le code du concepteur à moins que vous ne sachiez vraiment ce que vous faites) vous devrez éditer le formulaire.Designer.cs. Dans ce fichier, vous trouverez du code qui ressemble à ceci:

this.panelArea = new YourProject.MyPanel();

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

this.panelArea = new MyPanel(); 

Après avoir terminé ces étapes, mon programme de peinture ne scintille plus.

Pour toute autre personne ayant le même problème, le problème est enfin résolu.

Profitez!

50
répondu BigBug 2016-05-02 07:22:57

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 permet la double mise en mémoire tampon pour tous les contrôles depuis le niveau du formulaire vers le bas, sinon la double mise en mémoire tampon doit être activée individuellement pour chacun d'eux... vous pouvez affiner double bufferring après parce recouvert double mise en mémoire tampon peut donner des effets secondaires indésirables.

24
répondu user3641393 2016-09-28 16:02:22

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

protected override void OnPaint(PaintEventArgs e) {}

Ainsi Que

this.DoubleBuffered = true;

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

  1. peindre les choses dans le bon ordre!
  2. assurez-vous que votre fonction de tirage est

Winforms appelle la méthode OnPaint chaque fois que le formulaire doit être redessiné. Il y a plusieurs façons dont il peut être dévalidé, y compris le déplacement d'un le curseur de la souris sur le formulaire peut parfois appeler un événement redessiner.

Et note importante à propos de OnPaint, est que vous ne commencez pas à partir de zéro à chaque fois, vous commencez plutôt à partir de l'endroit où vous étiez, si vous remplissez la couleur d'arrière-plan, vous allez probablement obtenir 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 cher, et il doit être éliminé avant qu'il est recréé (la collecte des ordures ne le gère pas à 100% correctement ou dit la documentation). J'ai créé une variable de classe

protected Graphics gfx = null;

, puis utilisé localement dans OnPaint comme ça, mais c'était parce que j'avais besoin d'utiliser le gfx objet dans d'autres endroits dans ma classe. Sinon, NE PAS le FAIRE. Si vous ne peignez que dans OnPaint, veuillez utiliser e.Graphics!!

// clean up old graphics object
gfx.Dispose();

// recreate graphics object (dont use e.Graphics, because we need to use it 
// in other functions)
gfx = this.CreateGraphics();

J'espère que cela aide.

14
répondu ohmusama 2013-05-21 08:52:00

La double mise en mémoire tampon ne va pas être d'une grande aide ici, j'en ai peur. J'ai rencontré cela 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.

Faites du panneau d'origine que vous avez ( panelArea ) une zone transparente, et placez-le au-dessus d'un 2ème Panneau, que vous appelez panelDraw par exemple. Assurez-vous d'avoir panelArea en face. J'ai fouetté cela et il s'est débarrassé du scintillement, mais a laissé la forme qui était dessinée barbouillé 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 d'origine:

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 seulement repeindre le 'panelDraw' sur L'événement MouseUp.

// Use the panelDraw paint event to draw shapes that are done
void panelDraw_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelDraw.CreateGraphics();

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

// Use the panelArea_paint event to update the new shape-dragging...
private void panelArea_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelArea.CreateGraphics();

    if (drawSETPaint == 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;

    drawSETPaint = 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();
}
3
répondu Tom 2011-11-08 08:13:35

Je sais que c'est vraiment une vieille question mais peut-être que quelqu'un la trouvera utile.
Je voudrais faire un peu d'amélioration à la réponse de viper.

Vous pouvez faire une extension simple à la classe de panneau et masquer la propriété de réglage par réflexion.

public static class MyExtensions {

    public static void SetDoubleBuffered(this Panel panel) {
        typeof(Panel).InvokeMember(
           "DoubleBuffered",
           BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
           null,
           panel,
           new object[] { true });
    }
}

Si le nom de votre variable Panel est myPanel, vous pouvez simplement appeler
myPanel.SetDoubleBuffered ();
et c'est tout. Le Code semble beaucoup plus propre.

3
répondu thorgil 2013-10-29 08:20:15

Je conseillerais de surcharger OnPaintBackground et de gérer l'arrière-plan vous-même. Si vous savez que vous peignez tout le contrôle, vous ne pouvez rien faire dans OnPaintBackground (n'appelez pas la méthode de base) et cela empêchera la couleur d'arrière-plan d'être peinte en premier

2
répondu Matt 2011-11-08 06:46:08

Dans cette condition, vous devez activer le double tampon . Ouvrez le formulaire actuel et accédez aux propriétés du formulaire et appliquez double buffer true; ou vous pouvez également écrire ce code .

this.DoubleBuffered = true;     

Dans la charge de forme.

2
répondu 2015-07-07 12:57:40

Voici le programme de déplacement du cercle dans. net, qui ne clignote pas.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
namespace CircleMove
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        int x=0,y=0;
        Thread t;

        public MainForm()
        {

            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();

            //
            // TODO: Add constructor code after the InitializeComponent() call.
            //
        }
        void MainFormPaint(object sender, PaintEventArgs e)
        {
            Graphics g=e.Graphics;
            Pen p=new Pen(Color.Orange);
            Brush b=new SolidBrush(Color.Red);
        //  g.FillRectangle(b,0,0,100,100);
            g.FillEllipse(b,x,y,100,100);
        }
        void MainFormLoad(object sender, EventArgs e)
        {
            t=new Thread(  new ThreadStart(

                ()=>{
                    while(true)
                    {
                        Thread.Sleep(10);
                        x++;y++;
                        this.Invoke(new Action(
                            ()=>{

                                this.Refresh();
                                this.Invalidate();
                                this.DoubleBuffered=true;
                                }
                                            )
                                        );
                    }
                    }
                                            )

                        );

            t.Start();
        }
    }
}
1
répondu sanjay 2017-02-04 09:31:51

Si la mémoire est étroite (donc vous ne voulez pas le coût de la mémoire de la double mise en mémoire tampon), une façon possible de réduire, mais pas d'éliminer, le scintillement, est de définir la couleur d'arrière-plan à la couleur dominante dans votre scène actuelle.

Pourquoi cela aide: flicker est un flash momentané de la couleur d'arrière-plan, que le système d'exploitation dessine avant de dessiner des contrôles enfants ou votre code de dessin personnalisé. Si ce flash est une couleur qui est plus proche de la couleur finale à afficher, il sera moins perceptible.

Si vous êtes vous ne savez pas quelle couleur pour commencer, commencez avec 50% de gris, car il s'agit d'une moyenne de noir et blanc, donc sera plus proche de la plupart des couleurs de votre scène.

myFormOrControl.BackColor = Color.Gray;
0
répondu ToolmakerSteve 2016-10-09 13:46:24

Essayez d'insérer une logique de dessin dans

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
}
Méthode

. Dans ce cas, vous devez utiliser le paramètre e pour obtenir L'objet graphique. L'utilisation de e.Les graphiques de la propriété. Ensuite, vous devez appeler la méthode Invalidate() pour ce formulaire chaque fois que le formulaire doit être redessiné. PS: DoubleBuffered doit être défini sur true.

0
répondu DeeDee 2016-12-31 07:56:01

Si tout ce qui précède ne fonctionne pas, vous pouvez toujours créer votre propre double tampon lien vers le tutoriel Microsofts: https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-reduce-graphics-flicker-with-double-buffering-for-forms-and-controls

Espère que cela fonctionne pour vous

0
répondu Jan Tamis Kossen 2017-06-12 08:41:43

Pouvez-vous essayer d'utiliser une minuterie et un booléen pour vérifier si la souris est en panne, et peindre à cet endroit, en utilisant une variable à nouveau vérifier si l'Utilisateur a déplacé sa souris, si déplacé peindre cet endroit aussi, etc.

Ou vérifiez simplement si la souris vers le bas(via boolean qui définit true lorsque la souris est en panne)en utilisant une minuterie et peignez-la en considérant que vous essayez probablement de peindre un pixel, pas comme si vous aviez une ombre, etc. au lieu d'utiliser mousedown réelle. Donc, vous vérifiez toutes les 1 seconde au lieu de 0.0001 et il ne sera pas scintillement. Ou vice-versa, essayez-le avec vos propres temps.

-1
répondu Stormturtle 2011-11-08 07:40:51