Ich habe ein Programm, das im Wesentlichen wie eine Farbanwendung ist. Mein Programm hat jedoch einige flackernde Probleme. Ich habe die folgende Zeile in meinem Code (die das Flackern beseitigen sollte - aber nicht):
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
Mein Code (abzüglich der Super- und Unterklassen für die Formen lautet wie folgt:
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();
}
}
}
Wie stoppe ich das Flackern?
* UPDATE: * Dieser Code funktioniert wirklich gut, wenn ich direkt auf dem Formular zeichne. Wenn ich jedoch versuche, auf das Panel zu zeichnen, wird das Flackern zu einem Problem
this.DoubleBuffered = true;
?Antworten:
Für eine "sauberere Lösung" und um das Basisfenster weiterhin zu verwenden, können Sie einfach Reflection verwenden, um die doppelte Pufferung zu implementieren, indem Sie diesen Code dem Formular hinzufügen, das die Felder enthält, in die Sie zeichnen möchten
typeof(Panel).InvokeMember("DoubleBuffered", BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, DrawingPanel, new object[] { true });
Wobei "DrawingPanel" der Name des Bedienfelds ist, für das Sie die doppelte Pufferung durchführen möchten.
Ich weiß, dass seit der Frage ziemlich viel Zeit vergangen ist, aber dies könnte in Zukunft jemandem helfen.
quelle
Endlich das Flackern gelöst. Da ich anstelle des Formulars auf einer Tafel gezeichnet habe, löst die folgende Codezeile das Flackern nicht:
this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
SetStyle muss vom Typ 'YourProject.YourProject' sein (oder davon abgeleitet sein), daher müssen Sie eine Klasse als solche erstellen (damit Sie MyPanel verwenden können, das von SPaint.SPaint abgeleitet wird und somit Doublebuffering direkt für verwenden kann das Panel - und nicht das Formular):
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); } } }
Nachdem Sie dies getan haben (obwohl Sie den Designer-Code eigentlich nie bearbeiten sollten, es sei denn, Sie wissen wirklich, was Sie tun), müssen Sie die Form.Designer.cs bearbeiten. In dieser Datei finden Sie Code, der folgendermaßen aussieht:
this.panelArea = new YourProject.MyPanel();
Die obige Zeile muss geändert werden in:
this.panelArea = new MyPanel();
Nachdem ich diese Schritte ausgeführt habe, flackert mein Malprogramm nicht mehr.
Für alle anderen, die das gleiche Problem haben, ist das Problem endlich gelöst.
Genießen!
quelle
Kopieren Sie diese und fügen Sie sie in Ihr Projekt ein
protected override CreateParams CreateParams { get { CreateParams handleParam = base.CreateParams; handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED return handleParam; } }
Dies ermöglicht die Doppelpufferung für alle Steuerelemente ab der Formularebene. Andernfalls muss die Doppelpufferung für jedes Steuerelement einzeln aktiviert werden. Anschließend möchten Sie möglicherweise die Doppelpufferung optimieren, da eine doppelte Doppelpufferung unerwünschte Nebenwirkungen haben kann.
quelle
Ich hatte das gleiche Problem. Ich konnte mich nie zu 100% vom Flimmern befreien (siehe Punkt 2), aber ich habe es benutzt
protected override void OnPaint(PaintEventArgs e) {}
ebenso gut wie
this.DoubleBuffered = true;
Das Hauptproblem beim Flackern ist, dass Sie sich vergewissern
winforms ruft die
OnPaint
Methode jedes Mal auf , wenn das Formular neu gezeichnet werden muss. Es gibt viele Möglichkeiten, wie es entwertet werden kann. Wenn Sie einen Mauszeiger über das Formular bewegen, kann dies manchmal zu einem Neuzeichnungsereignis führen.Und ein wichtiger Hinweis
OnPaint
ist, dass Sie nicht jedes Mal von vorne anfangen, sondern dort beginnen, wo Sie waren. Wenn Sie die Hintergrundfarbe überfluten, werden Sie wahrscheinlich flackern.Endlich dein gfx Objekt. Im Inneren müssen
OnPaint
Sie das Grafikobjekt neu erstellen, jedoch NUR, wenn sich die Bildschirmgröße geändert hat. Die Neuerstellung des Objekts ist sehr teuer und muss vor der Neuerstellung entsorgt werden (die Speicherbereinigung behandelt es nicht zu 100% korrekt, heißt es in der Dokumentation). Ich habe eine Klassenvariable erstelltprotected Graphics gfx = null;
und dann lokal
OnPaint
so verwendet, aber das lag daran, dass ich das gfx-Objekt an anderen Stellen in meiner Klasse verwenden musste. Andernfalls tun Sie dies nicht. Wenn Sie nur in OnPaint malen, verwenden Sie bittee.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();
Hoffe das hilft.
quelle
protected override void OnPaint(PaintEventArgs e) {}
Objekt zu überschreiben. Das neue Objekt wird in Ihrer Liste der Elemente angezeigt, die in Ihr Formular eingefügt werden sollen. Sie können den Code auch manuell bearbeiten, um den Referenztyp zu ändern, wenn Sie das Bedienfeld bereits erstellt haben und nicht die einfache Möglichkeit haben, es einfach neu zu erstellen.e.Graphics
Hilfen zusammen mitthis.DoubleBuffered = true
. Durch die Verwendung von erstelltGraphics
erhöht der doppelte Puffer nicht nur den Flackereffekt, sondern macht dasControl
(oderForm
) während der meisten Zeit auch weiß.Doppelte Pufferung wird hier leider keine große Hilfe sein. Ich bin vor einiger Zeit darauf gestoßen und habe auf ziemlich ungeschickte Weise ein separates Panel hinzugefügt, aber es hat für meine Anwendung funktioniert.
Machen Sie das ursprüngliche Panel, das Sie haben (panelArea), zu einem transparenten Bereich und legen Sie es auf ein zweites Panel, das Sie beispielsweise panelDraw nennen. Stellen Sie sicher, dass panelArea vorne ist. Ich habe das ausgepeitscht und es hat das Flackern beseitigt, aber die Form, die gezeichnet wurde, ist verschmiert, so dass es auch keine vollständige Lösung ist.
Eine transparente Tafel kann erstellt werden, indem einige Malaktionen der ursprünglichen Tafel überschrieben werden:
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){} }
Die Idee ist, das Zeichnen der temporären Form während des MouseMove-Ereignisses der 'panelArea' zu handhaben und NUR das 'panelDraw' des MouseUp-Ereignisses neu zu malen.
// 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(); }
quelle
Ich würde empfehlen, OnPaintBackground zu überschreiben und den Hintergrund selbst zu löschen. Wenn Sie wissen, dass Sie das gesamte Steuerelement malen, können Sie in OnPaintBackground nichts tun (rufen Sie nicht die Basismethode auf), und es wird verhindert, dass die Hintergrundfarbe zuerst gezeichnet wird
quelle
Ich weiß, dass dies eine wirklich alte Frage ist, aber vielleicht findet es jemand nützlich.
Ich möchte die Antwort der Viper kaum verbessern.
Sie können die Panel-Klasse einfach erweitern und die Einstellungseigenschaft durch Reflektion ausblenden.
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 }); } }
Wenn der Name Ihrer Panel-Variablen myPanel ist, können Sie einfach
myPanel.SetDoubleBuffered () aufrufen .
und das ist es. Code sieht viel sauberer aus.
quelle
In diesem Zustand müssen Sie den Doppelpuffer aktivieren. Öffnen Sie das aktuelle Formular, gehen Sie zu den Formulareigenschaften und wenden Sie den doppelten Puffer true an. oder Sie können diesen Code auch schreiben.
this.DoubleBuffered = true;
In Form laden.
quelle
Hier ist das Programm zum Bewegen von Kreisen in .net, das nicht flackert.
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(); } } }
quelle
Wenn all dies nicht funktioniert, können Sie jederzeit einen eigenen Doppelpuffer-Link zum Microsoft-Lernprogramm erstellen: https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-reduce- Grafik-Flimmern mit doppelter Pufferung für Formulare und Steuerelemente
hofft, dass es für Sie funktioniert
quelle
Es ist eine alte Frage, aber um ganz zu sein: Es gibt noch eine andere Lösung, die bei mir funktioniert hat, bei der die Doppelpufferung nicht funktioniert hat.
Wie sich herausstellt, bietet Microsoft die BufferedGraphics- Klasse als Lösung an. Das Schöne an dieser Klasse ist, dass Sie damit ein Grafikobjekt in ein anderes kopieren können. Abgesehen davon, dass Sie ein temporäres Grafikobjekt einrichten und es schließlich an das endgültige Ziel kopieren, können Sie so ziemlich denselben Code verwenden, den Sie wann erstellt hätten Das Flackern sollte kein Problem gewesen sein:
private void Indicator_Paint(object sender, PaintEventArgs e) { Control pbIndicator = (Control)sender; Rectangle targetRect = pbIndicator.ClientRectangle; Image img = Bitmap.FromFile("bitmap.bmp"); BufferedGraphicsContext ctx = new BufferedGraphicsContext(); BufferedGraphics bg = ctx.Allocate(e.Graphics, targetRect); // Do the graphic stuff bg.Graphics.Clear(this.BackColor); bg.Graphics.DrawImage(img, 0, 0); // etcetera bg.Render(e.Graphics); bg.Dispose(); ctx.Dispose(); }
Nachteil dieser Lösung, dass es Ihren Code überladen könnte. Außerdem bin ich mir nicht sicher, ob es eine gute Idee ist, den Kontext jedes Mal einzurichten, oder ob es ausreichen würde, einen im Voraus zu erstellen und diesen Kontext weiterhin zu verwenden.
Weitere Informationen finden Sie unter https://docs.microsoft.com/en-us/dotnet/api/system.drawing.bufferedgraphicscontext?view=dotnet-plat-ext-3.1
quelle
Wenn der Speicher knapp ist (Sie möchten also nicht die Speicherkosten für die Doppelpufferung), können Sie das Flimmern möglicherweise reduzieren, aber nicht beseitigen, indem Sie die Hintergrundfarbe auf die dominierende Farbe in Ihrer aktuellen Szene einstellen.
Warum dies hilft: Flimmern ist ein kurzes Aufblitzen der Hintergrundfarbe, die das Betriebssystem zeichnet, bevor untergeordnete Steuerelemente oder Ihr benutzerdefinierter Zeichencode gezeichnet werden. Wenn dieser Blitz eine Farbe ist, die näher an der endgültigen anzuzeigenden Farbe liegt, fällt er weniger auf.
Wenn Sie sich nicht sicher sind, mit welcher Farbe Sie beginnen sollen, beginnen Sie mit 50% Grau, da dies ein Durchschnitt von Schwarzweiß ist und daher den meisten Farben in Ihrer Szene näher kommt.
quelle
Versuchen Sie, die Zeichnungslogik in die aktuellen Formulare einzufügen
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); }
Methode. In diesem Fall sollten Sie den Parameter e verwenden, um das Grafikobjekt abzurufen. Verwenden Sie die Eigenschaft e.Graphics. Dann sollten Sie die Invalidate () -Methode für dieses Formular aufrufen, wenn das Formular neu gezeichnet werden muss. PS: DoubleBuffered muss auf true gesetzt sein.
quelle
tun Sie einfach,
this.Refresh()
wenn das Formular angezeigt wird.quelle
Das Zeichnen auf ein Etikett anstelle eines Panels löste das Problem für mich.
Sie müssen weder DoubleBuffering noch etwas anderes verwenden.
Sie können den Text von der Beschriftung entfernen, AutoSize auf false setzen, ihn dann andocken oder die Größe festlegen und ihn wie für das Bedienfeld verwenden.
Die besten Wünsche,
quelle
Können Sie versuchen, mit einem Timer und einem Booleschen Wert zu überprüfen, ob die Maus gedrückt ist, und an dieser Stelle malen, indem Sie erneut eine Variable verwenden, um zu überprüfen, ob der Benutzer seine Maus bewegt hat, ob diese Stelle auch bewegt wurde usw.
Oder überprüfen Sie einfach mit einem Timer, ob die Maus gedrückt ist (über einen Booleschen Wert, der bei gedrückter Maus auf true gesetzt ist), und malen Sie ihn, wenn Sie wahrscheinlich nur ein Pixel malen möchten , nicht als hätten Sie Schatten usw. Anstatt die eigentliche Maus zu verwenden. Sie überprüfen also alle 1 Sekunde anstelle von 0,0001 und es flackert nicht. Oder versuchen Sie es umgekehrt mit Ihren eigenen Zeiten.
quelle