2010-04-09 5 views
9

Je travaille sur un programme de dessin, mais j'ai un problème de scintillement lorsque je déplace un curseur de souris tout en dessinant une ligne élastique. J'espère que vous pouvez me aider à supprimer cette vacillante de la ligne, voici le code:C# graphiques scintillants

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 GraphicsTest 
{ 
    public partial class Form1 : Form 
    { 
     int xFirst, yFirst; 
     Bitmap bm = new Bitmap(1000, 1000); 
     Graphics bmG; 
     Graphics xG; 
     Pen pen = new Pen(Color.Black, 1); 
     bool draw = false; 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      bmG = Graphics.FromImage(bm); 
      xG = this.CreateGraphics(); 
      bmG.Clear(Color.White); 
     } 

     private void Form1_MouseDown(object sender, MouseEventArgs e) 
     { 
      xFirst = e.X; 
      yFirst = e.Y; 
      draw = true; 
     } 

     private void Form1_MouseUp(object sender, MouseEventArgs e) 
     { 
      bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y); 
      draw = false; 
      xG.DrawImage(bm, 0, 0); 
     } 

     private void Form1_MouseMove(object sender, MouseEventArgs e) 
     { 
      if (draw) 
      { 
       xG.DrawImage(bm, 0, 0); 
       xG.DrawLine(pen, xFirst, yFirst, e.X, e.Y); 
      } 
     } 

     private void Form1_Paint(object sender, PaintEventArgs e) 
     { 
      xG.DrawImage(bm, 0, 0); 
     } 
    } 
} 
+3

La double mise en mémoire tampon est-elle activée dans vos formulaires ou vos contrôles? – FrustratedWithFormsDesigner

+3

Cela n'a pas aidé. – Jeff

Répondre

23

D'abord ne pas utiliser CreateGraphics() à moins que vous devez absolument. Liez un gestionnaire d'événements à OnPaint et appelez le Invalidate() lorsque vous souhaitez actualiser la surface.

Si vous ne voulez pas qu'il scintille, vous devrez doubler la surface de dessin. La méthode la plus simple consiste à définir la propriété DoubleBuffered de votre formulaire sur True.

Je vous le recommande vivement si vous envisagez d'étendre cette fonction pour dessiner sur le contrôle PictureBox. PictureBox est double-buffered par défaut et vous permet de contrôler votre région de dessin beaucoup plus simplement.

Dans le code:

public partial class Form1 : Form 
    { 
    int xFirst, yFirst; 
    Bitmap bm = new Bitmap(1000, 1000); 
    Graphics bmG; 
    Pen pen = new Pen(Color.Black, 1); 
    bool draw = false; 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     bmG = Graphics.FromImage(bm); 
     bmG.Clear(Color.White); 
    } 

    private void Form1_MouseDown(object sender, MouseEventArgs e) 
    { 
     xFirst = e.X; 
     yFirst = e.Y; 
     draw = true; 
    } 

    private void Form1_MouseUp(object sender, MouseEventArgs e) 
    { 
     bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y); 
     draw = false; 
     Invalidate(); 
    } 

    private void Form1_MouseMove(object sender, MouseEventArgs e) 
    { 
     if (draw) 
     { 
      Invalidate(); 
     } 
    } 

    private void Form1_Paint(object sender, PaintEventArgs e) 
    { 
     if (draw) { 
      e.Graphics.DrawImage(bm, 0, 0); 
      e.Graphics.DrawLine(pen, xFirst, yFirst, e.X, e.Y); 
     } else { 
      e.Graphics.DrawImage(bm, 0, 0); 
     } 
    } 
} 

Edit:

Une autre question, vous créez un membre Pen privé. Les stylos (et les pinceaux, ainsi que de nombreux objets GDI +) représentent des poignées vers des objets non gérés qui doivent être éliminés, sinon votre programme risque de fuir. Vous pouvez les inclure dans les instructions using (méthode préférée et sans risque) ou les éliminer explicitement dans la méthode Dispose du formulaire.

Alternativement, dans System.Drawing, vous pouvez accéder à des plumes et des pinceaux préconstruits qui n'ont pas besoin d'être (et ne devraient pas être) éliminés. Utilisez-les comme:

private void Form1_Paint(object sender, PaintEventArgs e) 
    { 
     if (draw) { 
      e.Graphics.DrawImage(bm, 0, 0); 
      e.Graphics.DrawLine(Pens.Black, xFirst, yFirst, e.X, e.Y); 
     } else { 
      e.Graphics.DrawImage(bm, 0, 0); 
     } 
    } 
+0

Merci pour une réponse et pour une suggestion, je vais essayer de le réparer. – Jeff

+0

J'ai corrigé un petit problème avec le code, vous pouvez vous débarrasser de votre objet xG et utiliser la propriété 'e.Graphics' de PaintEventArgs dans l'événement paint pour obtenir le contexte graphique de votre formulaire. –

+0

Oui, cela fonctionne. Merci beaucoup. Maintenant, je comprends l'utilisation de Invalidate() aussi. Agréable. – Jeff

6

La raison pour laquelle il scintille est que vous dessinez l'arrière-plan (qui est immédiatement affiché à l'écran, en essuyant la ligne) et superposez puis la ligne. Ainsi, la ligne ne cesse de disparaître et d'apparaître, donnant un affichage vacillant.

La meilleure solution à cela s'appelle Double Buffering. Qu'est-ce que vous faites est de dessiner l'image entière à un bitmap "hors écran", et ne montrez-le sur l'écran quand il est terminé. Comme vous n'affichez que l'image complétée, il n'y a aucun effet de scintillement. Vous devriez simplement pouvoir définir this.DoubleBuffered = true pour que WinForms fasse tout le travail pour vous. NB: Vous ne devriez pas vraiment dessiner en dehors de votre gestionnaire de peinture - idéalement, vous devez Invalider() la zone qui doit être redessinée, puis votre gestionnaire de peinture va redessiner juste cette zone (avec toutes les lignes superposées, etc.).

+0

Merci pour l'explication détaillée – Jeff

2

Code fixe et fonctionnel.

public partial class Form1 : Form 
{ 
    int x1, y1, x2, y2; 
    bool drag = false; 

    Bitmap bm = new Bitmap(1000, 1000); 
    Graphics bmg; 


    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     bmg = Graphics.FromImage(bm); 
    } 

    private void pictureBox_MouseDown(object sender, MouseEventArgs e) 
    { 
     drag = true; 
     x1 = e.X; 
     y1 = e.Y; 
    } 

    private void pictureBox_MouseUp(object sender, MouseEventArgs e) 
    { 
     drag = false; 

     bmg.DrawLine(Pens.Black, x1, y1, e.X, e.Y); 
     pictureBox.Invalidate(); 
    } 

    private void pictureBox_MouseMove(object sender, MouseEventArgs e) 
    { 
     if (drag) 
     { 
      x2 = e.X; 
      y2 = e.Y; 
      pictureBox.Invalidate(); 
     } 
    } 

    private void pictureBox_Paint(object sender, PaintEventArgs e) 
    { 
     if (drag) { 
      e.Graphics.DrawImage(bm, 0, 0); 
      e.Graphics.DrawLine(Pens.Black, x1, y1, x2, y2);    
     } 
     else { 
      e.Graphics.DrawImage(bm, 0, 0); 
     } 
    } 
} 
+0

a changé ma solution à ceci et mon mouvement de souris a cessé de fonctionner, lol – Pavel