come smettere di sfarfallio di Win # C #

Ho un programma che è essenzialmente come un’applicazione di pittura. Tuttavia, il mio programma ha alcuni problemi di sfarfallio. Ho la seguente riga nel mio codice (che dovrebbe sbarazzarsi di sfarfallio – ma non lo fa):

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

il mio codice (meno le classi super e sub per le forms è come segue:

 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 = eX; startPoint.Y = eY; drawSPaint = true; } private void panelArea_MouseMove(object sender, MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) { if (eX > startPoint.X) { rect.X = startPoint.X; rect.Width = eX - startPoint.X; } else { rect.X = eX; rect.Width = startPoint.X - eX; } if (eY > startPoint.Y) { rect.Y = startPoint.Y; rect.Height = eY - startPoint.Y; } else { rect.Y = eY; rect.Height = startPoint.Y - eY; } panelArea.Invalidate(); } } private void panelArea_MouseUp(object sender, MouseEventArgs e) { endPoint.X = eX; endPoint.Y = eY; 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(); } } } 

Come posso interrompere lo sfarfallio?

* AGGIORNAMENTO: * Questo codice funziona davvero bene quando sto disegnando direttamente sul modulo. Tuttavia, quando provo a disegnare sul pannello, lo sfarfallio diventa un problema

Finalmente risolto lo sfarfallio. Poiché stavo disegnando su un pannello al posto del modulo, la riga di codice seguente non risolverà lo sfarfallio:

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

SetStyle deve essere di tipo “YourProject.YourProject” (o derivato da esso) quindi, devi creare una class come tale (in modo che tu possa usare MyPanel che sarà derivato da SPaint.SPaint e quindi ti permetta di usare il doublebuffering direttamente per il pannello – piuttosto che la forma):

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

Dopo averlo fatto (anche se non dovresti davvero modificare il codice del designer se non sai veramente cosa stai facendo) dovrai modificare Form.Designer.cs. All’interno di questo file troverai il codice che assomiglia a questo:

 this.panelArea = new YourProject.MyPanel(); 

La riga sopra deve essere cambiata in:

 this.panelArea = new MyPanel(); 

Dopo aver completato questi passaggi, il mio programma di pittura non ha più sfarfallio.

Per chiunque altro abbia lo stesso problema, il problema è finalmente risolto.

Godere!

Per una “soluzione più pulita” e per continuare a utilizzare il pannello di base, è sufficiente utilizzare Reflection per implementare il doppio buffering, aggiungendo questo codice al modulo che contiene i pannelli in cui si desidera disegnare

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

Dove “DrawingPanel” è il nome del pannello in cui si desidera eseguire il doppio buffering.

So che è passato un bel po ‘di tempo da quando è stata posta la domanda, ma questo potrebbe aiutare qualcuno in futuro.

Copia e incolla questo nel tuo progetto

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

Ciò abilita il doppio buffering per tutti i controlli dal livello di forma in basso, altrimenti il ​​doppio buffering deve essere abilitato individualmente per ognuno … si consiglia di perfezionare il doppio buffering dopo che questo doppio buffering può generare effetti collaterali indesiderati.

Ho avuto lo stesso problema. Non sono mai riuscito a liberarmi al 100% del barlume (vedi punto 2), ma l’ho usato

 protected override void OnPaint(PaintEventArgs e) {} 

così come

 this.DoubleBuffered = true; 

Il problema principale per lo sfarfallio è assicurarti

  1. dipingi le cose nell’ordine giusto!
  2. assicurati che la tua funzione di disegno sia

winforms richiama il metodo OnPaint ogni volta che il modulo deve essere ridisegnato. Ci sono molti modi in cui può essere convalidato, incluso lo spostamento di un cursore del mouse sul modulo a volte può richiamare un evento di ridisegno.

E una nota importante su OnPaint è che non si ricomincia da capo ogni volta, si avvia invece da dove si trovava, se si riempie il colore dello sfondo, è probabile che si verifichi uno sfarfallio.

Finalmente il tuo object gfx. All’interno di OnPaint è necessario ricreare l’object grafico, ma SOLO se le dimensioni dello schermo sono cambiate. ricreare l’object è molto costoso e deve essere eliminato prima di essere ricreato (la raccolta dei dati inutili non è gestita correttamente al 100% o la documentazione è così). Ho creato una variabile di class

 protected Graphics gfx = null; 

e poi l’ho usato localmente in OnPaint in OnPaint modo, ma questo perché avevo bisogno di usare l’object gfx in altre posizioni nella mia class. Altrimenti NON FARE QUESTO. Se dipingi solo su OnPaint, usa 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(); 

Spero che questo ti aiuti.

Il doppio buffering non sarà di grande aiuto qui, temo. Mi sono imbattuto in questo poco fa e ho finito per aggiungere un pannello separato in un modo piuttosto goffo, ma ha funzionato per la mia applicazione.

Rendi il pannello originale che hai (panelArea) un’area trasparente, e mettilo sopra un secondo pannello, che tu chiami panelDraw per esempio. Assicurati di avere panelArea davanti. L’ho frustato e mi sono liberato dello sfarfallio, ma ho lasciato la forma che veniva disegnata in modo che non fosse una soluzione completa.

Un pannello trasparente può essere eseguito sovrascrivendo alcune azioni di pittura dal pannello originale:

 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’idea è di gestire il disegno della forma temporanea durante l’evento MouseMove del ‘panelArea’ e SOLO ridisegnare il ‘panelDraw’ sull’evento 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 = eX; endPoint.Y = eY; 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(); } 

So che questa è una domanda molto vecchia ma forse qualcuno la troverà utile.
Mi piacerebbe apportare un piccolo miglioramento alla risposta di Viper.

È ansible effettuare un’estensione semplice alla class Panel e hide la proprietà delle impostazioni tramite il reflection.

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

Se il nome della variabile Panel è myPanel, puoi semplicemente chiamare
myPanel.SetDoubleBuffered ();
e questo è tutto. Il codice sembra molto più pulito.

Ti consiglio di ignorare OnPaintBackground e gestire lo sfondo da solo. Se sai che stai dipingendo l’intero controllo non puoi fare nulla in OnPaintBackground (non chiamare il metodo base) e impedirà che il colore di sfondo venga dipinto per primo

In questa condizione è necessario abilitare il doppio buffer. Apri il modulo corrente e vai a formare le proprietà e applica il doppio buffer true; oppure puoi anche scrivere questo codice.

 this.DoubleBuffered = true; 

Nel caricamento del modulo.

ecco il programma di spostamento del cerchio in .net, che non sfarfalla.

 using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using System.Threading; namespace CircleMove { ///  /// Description of MainForm. ///  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(); } } } 

Se la memoria è stretta (quindi non si desidera il costo di memoria del doppio buffering), un ansible modo per RIDURRE, anche se non eliminare, lo sfarfallio, è impostare il colore di sfondo sul colore dominante nella scena corrente.

Perché questo aiuta: lo sfarfallio è un lampo momentaneo del colore di sfondo, che il sistema operativo disegna prima di disegnare i controlli figlio o il codice del disegno personalizzato. Se il flash è un colore più vicino al colore finale da visualizzare, sarà meno evidente.

Se non sei sicuro di quale colore iniziare, inizia con il 50% di grigio, perché è una media di bianco e nero, quindi sarà più vicino alla maggior parte dei colors nella scena.

 myFormOrControl.BackColor = Color.Gray; 

Prova ad inserire la logica di disegno nella forma corrente

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

metodo. In questo caso dovresti usare il parametro e per ottenere l’object Graphics. Usa la proprietà e.Graphics. Quindi dovresti invocare il metodo Invalidate () per questo modulo ogni volta che il modulo deve essere ridisegnato. PS: DoubleBuffered deve essere impostato su true.

se tutto ciò non funziona, puoi sempre creare il tuo link buffer doppio al tutorial di Microsofts: https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-reduce- grafica-flicker-con-il doppio buffer-per-forms-e-controlli

spera che funzioni per te

Puoi provare a usare un timer e booleano per controllare se il mouse è inattivo, e dipingere in quel punto, usando di nuovo una variabile controlla se l’utente ha spostato il suo mouse, se è stato spostato dipingi quel punto troppo ecc.

O semplicemente controlla se il mouse è in basso (tramite booleano che imposta true quando il mouse è inattivo) usando un timer e dipingilo considerando che probabilmente stai provando a dipingere un pixel, non come se avessi un’ombra, ecc. Invece di usare il mouse effettivo. Quindi controlli ogni 1 secondo invece di 0.0001 e non tremolerà. O viceversa, provalo con i tuoi tempi.