web-dev-qa-db-fra.com

comment arrêter les winforms C # scintillants

J'ai un programme qui ressemble essentiellement à une application Paint. Cependant, mon programme a des problèmes vacillants. 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 classes super et sub pour les formes est la suivante:

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 scintillement?

* UPDATE: * Ce code fonctionne réellement 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

61
BigBug

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 dérivé de celui-ci). Vous devez donc créer une classe en tant que telle (pour pouvoir utiliser MyPanel, qui sera dérivé de SPaint.SPaint et vous permettra donc d'utiliser directement le double tampon pour le panneau - plutôt que la forme):

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);
        }
    }
}

Une fois que vous avez fait cela (même si vous ne devriez vraiment jamais éditer le code du concepteur à moins de savoir vraiment ce que vous faites), vous devez éditer Form.Designer.cs. Dans ce fichier, vous trouverez un code qui ressemble à ceci:

this.panelArea = new YourProject.MyPanel();

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

this.panelArea = new MyPanel(); 

Une fois ces étapes terminées, mon programme Paint ne clignote plus. 

Pour quiconque ayant le même problème, le problème est finalement résolu. 

Prendre plaisir!

52
BigBug

Pour une "solution plus propre" et 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 au formulaire qui contient les panneaux dans lesquels vous souhaitez attirer

    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 utiliser pour la mise en mémoire tampon double.

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.

54
viper

Copier et coller ceci dans votre projet

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

Cela active la mise en mémoire tampon double pour tous les contrôles à partir du niveau de formulaire, sinon la double mise en mémoire tampon doit être activée individuellement pour chacun d'entre eux ... vous pouvez affiner le réglage de la mise en mémoire tampon double après cela car la double mise en mémoire tampon masquée peut produire des effets secondaires indésirables.

26
user3641393

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

protected override void OnPaint(PaintEventArgs e) {}

aussi bien que

this.DoubleBuffered = true;

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

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

winforms appelle la méthode OnPaint chaque fois que le formulaire doit être redessiné. Il peut être dévalidé de nombreuses manières. Le déplacement du curseur de la souris sur le formulaire peut parfois invoquer un événement de redessinage.

Et note importante à propos de OnPaint, si vous ne partez pas de zéro à chaque fois, vous partez de là où vous étiez. Si vous remplissez la couleur de fond, vous risquez de scintiller.

Enfin votre objet gfx. Dans OnPaint, vous devrez recréer l'objet graphique, mais UNIQUEMENT si la taille de l'écran a changé. recréer l'objet est très coûteux et il doit être éliminé avant de le recréer (la récupération de place ne la gère pas à 100% correctement ou ne contient pas de documentation). J'ai créé une variable de classe

protected Graphics gfx = null;

et ensuite utilisé localement dans OnPaint comme tel, mais c’est parce que j’avais besoin d’utiliser l’objet gfx dans d’autres emplacements de ma classe. Sinon, ne le faites pas. Si vous peignez uniquement 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 t'aides.

14
ohmusama

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

Vous pouvez créer une extension simple à la classe Panel et masquer la propriété de paramétrage 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. Code semble beaucoup plus propre.

3
thorgil

La double mise en mémoire tampon ne sera pas d'une grande aide ici, j'en ai bien peur. Je me suis heurté à cela il y a quelque temps et j'ai fini par ajouter un panneau séparé de 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 deuxième panneau, que vous appelez panelDraw par exemple. Assurez-vous d'avoir panelArea devant. J'ai fouetté ça et ça s'est débarrassé du scintillement, mais j'ai laissé la forme qui était en train d'être dessinée afin que ce ne soit pas une solution complète non plus.

Un panneau transparent peut être créé en remplaçant certaines actions de Paint 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 de 'panelArea' et de repeindre UNIQUEMENT le 'panelDraw' de 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
Tom

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

this.DoubleBuffered = true;     

En charge de formulaire.

2
user2922221

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

2
Matt

voici le programme de déplacement de cercle dans .net, qui ne scintille 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
sanjay

Essayez d'insérer la logique de dessin dans la forme actuelle 

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 Graphics. Utilisez la propriété e.Graphics. Vous devez ensuite 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
DeeDee

Si la mémoire est saturée (pour éviter les coûts de mémoire liés à la double mise en mémoire tampon), un moyen possible de RÉDUIRE, sans toutefois éliminer le scintillement, consiste à définir la couleur d'arrière-plan sur la couleur dominante de votre scène actuelle.

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

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

myFormOrControl.BackColor = Color.Gray;
0
ToolmakerSteve

il suffit de faire this.Refresh() lorsque le formulaire est affiché. 

0
Jam