intermitente - doble buffer c#
cómo dejar de parpadear C#winforms (13)
¿Puedes intentar usar un temporizador y un valor booleano para verificar si el mouse está abajo y pintar en ese lugar, usando una variable otra vez? Verificar si el usuario ha movido su mouse, si ha movido pintar ese spot también, etc.
O simplemente verifique si el mouse está abajo (a través de booleano que se establece como verdadero cuando el mouse está abajo) con un temporizador y píntelo, ya que probablemente esté tratando de pintar un píxel, no como si tuviera sombra, etc. En lugar de usar el mousedown real. Así que verifica cada 1 segundo en lugar de 0.0001 y no parpadeará. O viceversa, pruébalo con tus propios tiempos.
Tengo un programa que es esencialmente como una aplicación de pintura. Sin embargo, mi programa tiene algunos problemas de parpadeo. Tengo la siguiente línea en mi código (que debería eliminar el parpadeo, pero no lo hace):
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
Mi código (menos las súper y subclases de las formas es el siguiente:
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();
}
}
}
¿Cómo paro el parpadeo?
* ACTUALIZACIÓN: * Este código funciona realmente bien cuando estoy dibujando directamente en el formulario. Sin embargo, cuando intento dibujar en el panel, el parpadeo se convierte en un problema
Aquí está el programa de mover el círculo en .net, que no parpadea.
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();
}
}
}
Copia y pega esto en tu proyecto
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return handleParam;
}
}
Esto permite doble búfer para todos los controles desde el nivel de formulario hacia abajo, de lo contrario, el búfer doble debe habilitarse individualmente para cada uno ... es posible que desee ajustar el búfer doble después de esto, ya que el búfer doble cubierto puede producir efectos secundarios no deseados.
En esta condición tienes que habilitar doble búfer. Abra el formulario actual y vaya a las propiedades del formulario y aplique el búfer true O también puedes escribir este código.
this.DoubleBuffered = true;
En forma de carga.
Finalmente resolvió el parpadeo. Como estaba dibujando en un panel en lugar de la forma, la línea de código a continuación no resolverá el parpadeo:
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,
true);
SetStyle debe ser del tipo ''YourProject.YourProject'' (o derivado de él), por lo tanto, debe crear una clase como tal (de modo que pueda usar MyPanel, que se derivará de SPaint.SPaint y, por lo tanto, le permitirá usar el doble biberón directamente para el panel - en lugar del formulario):
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);
}
}
}
Después de hacer esto (aunque nunca deberías editar el código del diseñador a menos que realmente sepas lo que estás haciendo), tendrás que editar el Form.Designer.cs. Dentro de este archivo encontrarás un código que se ve así:
this.panelArea = new YourProject.MyPanel();
La línea anterior necesita ser cambiada a:
this.panelArea = new MyPanel();
Después de completar estos pasos, mi programa de pintura ya no parpadea.
Para cualquier otra persona que tenga el mismo problema, el problema finalmente se resuelve.
¡Disfrutar!
He tenido el mismo problema. Nunca fui capaz de deshacerme del flicker al 100% (vea el punto 2), pero utilicé esto
protected override void OnPaint(PaintEventArgs e) {}
tanto como
this.DoubleBuffered = true;
El principal problema para el parpadeo es asegurarse de que
- Pintar las cosas en el orden correcto!
- Asegúrate de que tu función de dibujo sea <alrededor de 1/60 de segundo
winforms invoca el método OnPaint
cada vez que se debe volver a dibujar el formulario. Hay muchas maneras en que puede ser desvalorizado, incluso mover un cursor del mouse sobre el formulario a veces puede invocar un evento de redibujado.
Y una nota importante sobre OnPaint
, es que no empiezas desde cero cada vez, sino que empiezas desde donde estabas, si llenas el color de fondo, es probable que tengas parpadeos.
Finalmente tu objeto gfx. Dentro de OnPaint
, deberá volver a crear el objeto gráfico, pero SOLO si el tamaño de la pantalla ha cambiado. recrear el objeto es muy costoso, y debe eliminarse antes de recrearlo (la recolección de basura no lo maneja al 100% correctamente o, por lo tanto, indica la documentación). Creé una variable de clase
protected Graphics gfx = null;
y luego lo usé localmente en OnPaint
como tal, pero esto fue porque necesitaba usar el objeto gfx en otras ubicaciones de mi clase. De lo contrario NO HAGAS ESTO. Si solo estás pintando en OnPaint, por favor 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();
Espero que esto ayude.
Me temo que el doble búfer no será de mucha ayuda aquí. Me encontré con esto hace un tiempo y terminé agregando un panel separado de una manera bastante torpe pero funcionó para mi aplicación.
Haga que el panel original que tiene (panelArea) sea un área transparente, y colóquelo en la parte superior de un segundo panel, al que llama panelDraw, por ejemplo. Asegúrese de tener panelArea en frente. Saqué esto rápidamente y se deshizo del parpadeo, pero dejé la forma dibujada manchada por lo que tampoco es una solución completa.
Se puede hacer un panel transparente al anular algunas acciones de pintura del panel original:
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){}
}
La idea es manejar el dibujo de la forma temporal durante el evento MouseMove de ''panelArea'' y SOLAMENTE volver a pintar el ''panelDraw'' en el 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 = 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();
}
Para una "solución más limpia" y para seguir usando el Panel base, simplemente puede usar Reflection para implementar el búfer doble, agregando este código al formulario que contiene los paneles en los que desea dibujar
typeof(Panel).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, DrawingPanel, new object[] { true });
Donde "DrawingPanel" es el nombre del panel en el que desea realizar el búfer doble.
Sé que ha pasado bastante tiempo desde que se hizo la pregunta, pero esto podría ayudar a alguien en el futuro.
Recomendaría anular OnPaintBackground y manejar el borrado de fondo. Si sabe que está pintando todo el control, no puede hacer nada en OnPaintBackground (no llame al método base) y evitará que el color de fondo se pinte primero.
Sé que esta es una pregunta muy antigua, pero quizás a alguien le resulte útil.
Me gustaría hacer una pequeña mejora a la respuesta de la víbora.
Puede hacer una extensión simple a la clase Panel y ocultar la propiedad de configuración a través de la reflexión.
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 el nombre de la variable de su Panel es myPanel, simplemente puede llamar
myPanel.SetDoubleBuffered ();
y eso es. El código se ve mucho más limpio.
Si la memoria está apretada (por lo que no quiere que el costo de la memoria intermedia doble), una forma posible de REDUCIR, aunque no eliminar, parpadear, es establecer el color de fondo en el color dominante en su escena actual.
Por qué esto ayuda: el parpadeo es un destello momentáneo del color de fondo, que el sistema operativo dibuja antes de dibujar controles secundarios o su código de dibujo personalizado. Si ese flash es un color que está más cerca del color final que se mostrará, será menos notable.
Si no está seguro de con qué color comenzar, comience con un 50% de gris, porque este es un promedio de blanco y negro, por lo que estará más cerca de la mayoría de los colores de la escena.
myFormOrControl.BackColor = Color.Gray;
Si todo lo anterior no funciona, siempre puede crear su propio enlace de doble búfer para el tutorial de Microsofts: https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-reduce-graphics-flicker-with-double-buffering-for-forms-and-controls
espera que te funcione
Trate de insertar la lógica de dibujo en la forma actual
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
método. En este caso debes usar el parámetro e para obtener el objeto Graphics. Usa la propiedad e.Graphics. Luego, debe invocar el método Invalidate () para este formulario siempre que se deba volver a dibujar. PS: DoubleBuffered se debe establecer en true.