Winforms Graphics flickering. (Double buffering doesn't help!) - c#

I'm trying to create a simple Windows Forms graphics app that basically will draw a circle every time the user clicks and it expands, while slowly fading away.
When I tried to use the Paint() Event for my graphics functionality, nothing happened, so I created a separate function called "Render" that is called in my main update Timer.
The app worked but the graphics flickered. After some researched I realized that I had to enable Double Buffering so that it would render to a buffer and then the buffer would be rendered to the screen.
The flickering still didn't stop!
Is this because double buffering only works for Paint() events and if so how do I get the Paint() event to work or am I not enabling Double Buffering right?
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Widget
{
public class Circle
{
public float X;
public float Y;
public float Radius;
public int Alpha;
public Circle(float X, float Y, float Radius, int Alpha)
{
this.X = X;
this.Y = Y;
this.Radius = Radius;
this.Alpha = Alpha;
}
}
public partial class Form1 : Form
{
public static readonly int ScreenX = Screen.PrimaryScreen.Bounds.Width;
public static readonly int ScreenY = Screen.PrimaryScreen.Bounds.Height;
public int WindowWidth = 500, WindowHeight = 500;
public Graphics G;
private Pen Pen;
private Timer timer = new Timer();
private List<Circle> Circles;
public Form1()
{
this.Text = "Widget - Sam Brandt";
this.Size = new Size(WindowWidth, WindowHeight);
this.StartPosition = FormStartPosition.Manual;
this.Location = new Point(ScreenX - WindowWidth - 100, 0);
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.Icon = new Icon("C:\\Users\\Admin\\Desktop\\Code Repositories\\Visual Studios\\Widget\\Widget\\Properties\\WidgetIcon.ico");
Pen = new Pen(Color.Black, 1);
G = CreateGraphics();
//this.Paint += new PaintEventHandler(OnPaint);
ConstructMouse();
FormWithTimer();
DoubleBuffered = true;
Circles = new List<Circle>();
}
public void ConstructMouse()
{
this.MouseUp += new MouseEventHandler(OnMouseUp);
this.MouseMove += new MouseEventHandler(OnMouseMove);
this.MouseDown += new MouseEventHandler(OnMouseDown);
}
public void FormWithTimer()
{
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = (10);
timer.Enabled = true;
timer.Start();
}
protected void OnMouseUp(object sender, MouseEventArgs e)
{
}
protected void OnMouseMove(object sender, MouseEventArgs e)
{
}
public void OnMouseDown(object sender, MouseEventArgs e)
{
Circles.Add(new Circle(e.Location.X, e.Location.Y, 0, 255));
}
/*public void OnPaint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
for (int i = 0; i < Circles.Count; i++)
{
Circle C = Circles[i];
e.Graphics.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
}
}*/
private void Tick()
{
for (int i = 0; i < Circles.Count; i++)
{
Circle C = Circles[i];
C.Radius++;
C.Alpha -= 3;
if (C.Alpha == 0)
{
Circles.RemoveAt(i);
}
}
}
public void Render()
{
G.Clear(Color.White);
for (int i = 0; i < Circles.Count; i++)
{
Circle C = Circles[i];
G.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
}
}
public void timer_Tick(object sender, EventArgs e)
{
Render();
Tick();
}
}
}

Short answer - keep DoubleBuffered = true and use Paint event.
When I tried to use a PaintEvent for my graphics functionality, nothing happened
When you do some modifications and want to reflect them, use Control.Invalidate method, which according to the documentation
Invalidates the entire surface of the control and causes the control to be redrawn.
In your case, something like this
void timer_Tick(object sender, EventArgs e)
{
Tick();
Invalidate();
}

More observations here but probably will be the answer.
Why Timer?
Use the Paint event, it is called when the GDI+ determines it needs to, you are constantly painting with your code as-is.
Your code makes it look like you are not using double buffering.

I'd do all your drawing to a separate Graphics object, and then copy that to your main Graphics object on the timer tick event only if there's been a change. You may need to keep track of that with a boolean member. This means your background drawing will have to be triggered by some other event.
If your picture is actually changing every 10 milliseconds, I'd slow down the timer a bit and set it to 50 milliseconds.

Try this (in VB):
Dim aProp = GetType(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
aProp.SetValue(Me, True, Nothing)
Also had problems with double buffering, and usual setting property DoubleBuffered to true do not work. I have found this solution somewhere on web

Related

Real-time draw.lines using serial port data

I want my simple program to take X and Y coordinates from serial port and draw them in window (like paint but with other device than mouse).
I managed to create code that recive and transforms data however I can't handle drawing that data on form.
Program works until Form1 window shows up. Then i recieve only "data_recived" and data value in console but rest of the datarecived event don't execute.
I know it is something wrong with DataReceivedHandler but i tried so many solution and none of them worked. (in comments you can see my attempts to use timer trigerred event to do that).
Could anyone give me at least some tips how to solve my problem? I would be really grateful
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.IO.Ports;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public string data;
private List<Point> points;
private Bitmap bmp;
private Pen pen;
private PictureBox pictureBox1 = new PictureBox();
//private System.Timers.Timer _timer;
//private DateTime _startTime;
public Form1()
{
InitializeComponent();
SerialPort serialPort1 = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
DoubleBuffered = true;
pen = new Pen(Color.Black, 3);
points = new List<Point>();
// _startTime = DateTime.Now;
// _timer = new System.Timers.Timer(100); // 0.1 s
// _timer.Elapsed += new System.Timers.ElapsedEventHandler (timer_Elapsed);
// _timer.Start();
// Console.WriteLine("Czas Start");
}
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort1 = (SerialPort)sender;
Console.WriteLine("data_recived");
data = serialPort1.ReadLine();
Console.WriteLine(data);
pointlist_reciver();
}
// void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
//{
// Console.WriteLine("Czas");
// TimeSpan timeSinceStart = DateTime.Now - _startTime;
//string output = string.Format("{0},{1}\r\n", DateTime.Now.ToLongDateString(), (int)Math.Floor(timeSinceStart.TotalMinutes));
// pointlist_reciver();
// }
public void pointlist_reciver()
{
int x1;
int y1;
points = new List<Point>();
string[] coordinates = new string[2];
coordinates = data.Split(',');
string x = coordinates[0];
string y = coordinates[1];
Int32.TryParse(x, out x1);
Int32.TryParse(y, out y1);
points.Add(new Point(x1, y1));
if (points.Count >= 2)
{
this.Paint += new PaintEventHandler(Form1_Paint);
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
PaintP(e);
}
public void PaintP ( PaintEventArgs e)
{
bmp = new Bitmap(1500, 1500);
using (Graphics g = Graphics.FromImage(bmp))
g.Clear(Color.White);
e.Graphics.DrawLines(pen, points.ToArray());
points.Clear();
}
}
}
points.Count >= 2
would never happen
this.Paint += new PaintEventHandler(Form1_Paint)
this does not make sense here, you need to register it only once on form load. What you are looking for is Invalidate(). Just beware you may be on different thread, so may need to Invoke first.
bmp = new Bitmap(1500, 1500);
using (Graphics g = Graphics.FromImage(bmp))
g.Clear(Color.White);
e.Graphics.DrawLines(pen, points.ToArray());
points.Clear();
you create bitmap you do not even use. Not sure what exactly you are trying to achieve here.
Example
You have two options. Either you draw on bitmap and paste that bitmap on the form. Or you just draw on the form. I made a simple example how to use the latter option. When you move mouse on the form it draws points. Just register the eventhandlers - Load, MouseMove, Paint.
List<Point> points = new List<Point>();
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
points.Add(e.Location);
Invalidate();
}
private void Form1_Load(object sender, EventArgs e)
{
this.DoubleBuffered = true;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
int radius = 3;
for (int i = points.Count - 1; i >= 0; --i)
{
Point p = points[i];
p.Y += 1;
if (p.Y > Height)
{
points.RemoveAt(i);
continue;
}
points[i] = p;
e.Graphics.FillEllipse(
Brushes.Red,
p.X - radius,
p.Y - radius,
2 * radius,
2 * radius
);
}
}
I managed to make my code work thanks to your suggestions. It still needs some work becasue it draws seperate lines instead of continuous one.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.IO.Ports;
using System.Threading;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public string data;
private List<Point> points = new List<Point>();
public string[] coordinates;
private Pen pen;
public Bitmap obrazek;
public int i = 0;
public Form1()
{
InitializeComponent();
obrazek = new Bitmap(1000, 1000);
using (Graphics g = Graphics.FromImage(obrazek))
g.Clear(Color.White);
SerialPort serialPort1 = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
DoubleBuffered = true;
pen = new Pen(Color.Black, 3);
points = new List<Point>();
}
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort TempSerialPort = (SerialPort)sender;
//Console.WriteLine("data_recived");
data = TempSerialPort.ReadLine();
Console.WriteLine(data);
pointlist_reciver();
//this.Invalidate();
pictureBox2.Invalidate();
}
public void pointlist_reciver()
{
int x1;
int y1;
string[] coordinates = data.Split(',');
string x = coordinates[0];
string y = coordinates[1];
Int32.TryParse(x, out x1);
Int32.TryParse(y, out y1);
points.Add(new Point(x1, y1));
Console.WriteLine(points.Count.ToString());
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(obrazek, 0, 0);
if (points.Count >= 2)
{
List<Point> points2 = points.GetRange(0, 2);
using (Graphics g = Graphics.FromImage(obrazek))
g.DrawLines(pen, points2.ToArray());
points.Clear();
}
}
}
}
Later i will post final version.

C# How to Move A Circle Along a Rectangle Path

I need to move a point along a rectangle path at the command of a button. I want it to start at the upper right corner of the rectangle path, but I am not sure how to get it to go all the way around the path and stop at the original point. The screen refreshes at the speed provided by the user in an input box. Thank you in advance!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Timers;
namespace Assignment_2
{
public partial class Form1 : Form
{
private const int formwidth = 1280;
private const int formheight = 720;
private const int ball_a_radius = 10;
private const int horizontaladjustment = 8;
private const double ball_a_distance_moved_per_refresh = 1.6;
private double ball_a_real_coord_x = 515;
private double ball_a_real_coord_y = 40;
private int ball_a_int_coord_x;
private int ball_a_int_coord_y;
private const double graphicrefreshrate = 30.0;
private static System.Timers.Timer graphic_area_refresh_clock = new System.Timers.Timer();
private static System.Timers.Timer ball_a_control_clock = new System.Timers.Timer();
private bool ball_a_clock_active = false;
public double speed = 0;
public Form1()
{
InitializeComponent();
ball_a_int_coord_x = (int)(ball_a_real_coord_x);
ball_a_int_coord_y = (int)(ball_a_real_coord_y);
System.Console.WriteLine("Initial coordinates: ball_a_int_coord_x = {0}. ball_a_int_coord_y = {1}.",
ball_a_int_coord_x, ball_a_int_coord_y);
graphic_area_refresh_clock.Enabled = false;
graphic_area_refresh_clock.Elapsed += new ElapsedEventHandler(Updatedisplay);
ball_a_control_clock.Enabled = false;
ball_a_control_clock.Elapsed += new ElapsedEventHandler(Updateballa);
Startgraphicclock(graphicrefreshrate);
Startballaclock(speed);
}
public class NumericTextBox : TextBox
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
}
private void panel2_Paint_1(object sender, PaintEventArgs e)
{
//Create pen
Pen blackPen = new Pen(Color.Black, 1);
//Create rectangle
Rectangle rect = new Rectangle(125, 50, 400, 400);
//Draw rectangle to screen
e.Graphics.DrawRectangle(blackPen, rect);
Graphics graph = e.Graphics;
graph.FillEllipse(Brushes.Green, ball_a_int_coord_x, ball_a_int_coord_y, 2 * ball_a_radius, 2 * ball_a_radius);
base.OnPaint(e);
}
public void button7_Click(object sender, EventArgs e)
{
speed = Convert.ToInt32(textBox3.Text);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
}
protected void Startgraphicclock(double refreshrate)
{
double elapsedtimebetweentics;
if (refreshrate < 1.0) refreshrate = 1.0;
elapsedtimebetweentics = 1000.0 / refreshrate;
graphic_area_refresh_clock.Interval = (int)System.Math.Round(elapsedtimebetweentics);
graphic_area_refresh_clock.Enabled = true;
}
protected void Startballaclock(double updaterate)
{
double elapsedtimebetweenballmoves;
if (updaterate < 1.0) updaterate = 1.0;
elapsedtimebetweenballmoves = 1000.0 / updaterate;
ball_a_control_clock.Interval = (int)System.Math.Round(elapsedtimebetweenballmoves);
ball_a_control_clock.Enabled = true;
ball_a_clock_active = true;
}
protected void Updatedisplay(System.Object sender, ElapsedEventArgs evt)
{
Invalidate();
if (!(ball_a_clock_active))
{
graphic_area_refresh_clock.Enabled = false;
System.Console.WriteLine("The graphical area is no longer refreshing. You may close the window.");
}
}
protected void Updateballa(System.Object sender, ElapsedEventArgs evt)
{
ball_a_real_coord_x = ball_a_real_coord_x - 5;
ball_a_real_coord_y = ball_a_real_coord_y - 5;
ball_a_int_coord_x = (int)System.Math.Round(ball_a_real_coord_x);
ball_a_int_coord_y = (int)System.Math.Round(ball_a_real_coord_y);
if (ball_a_int_coord_x >= formwidth || ball_a_int_coord_y + 2 * ball_a_radius <= 0 || ball_a_int_coord_y >= formheight)
{
ball_a_clock_active = false;
ball_a_control_clock.Enabled = false;
System.Console.WriteLine("The clock controlling ball a has stopped.");
}
}
private void button4_Click(object sender, EventArgs e)
{
ball_a_control_clock.Enabled = true;
}
}
}
I have a more straightforward way to move a circle. Your code is too long for me to read. See if you like this!
If I were you, I would use a PictureBox. I first create an image of a circle, and then put that image in the PictureBox. Then you can just use a timer to change the position of the PictureBox.
You should set the Interval of the timer to 33 ms, which is roughly 30 fps. This is how you would programme the timer:
Keep a counter to indicate how many pixels the circle has moved. Let's say you want it to move in a 100px x 50px rectangular path.
For every 33ms,
If the counter is less than 100,
increase the X position and the counter by 1,
if the counter is between 101 and 150,
increase the Y position and the counter by 1,
if the counter is between 151 and 250,
decrease the X position by 1 and increment the counter
if the counter is between 251 and 300,
decrease the Y position by 1 and increment the counter
if the counter is greater than 300,
stop the timer
I really don't like drawing stuff on the screen with the OnPaint event. I mean, you are moving a ball! People think of this as changing the x and y positions of a ball, not as deleting the ball at the previous position and drawing it in the new position. Changing the position of the picture box just makes LOTS more sense, don't you think so?

Calling a method from another class inside Panel_Paint does not draw anything

So this should be very simple but I have looked at some similar questions and can't find an answer.
I have a Form1 class and a Resistor class. Inside the Form1 class I have a Panel(I changed the name to Canvas), inside the Canvas_Paint method I am calling the method Draw from the Resistor class but is not drawing anything.
Form1 Class:
public partial class Form1 : Form
{
static float lineWidth = 2.0F;
static float backgroundLineWidth = 2.0F;
static Pen pen = new Pen(Color.Yellow, lineWidth);
static Pen backgroundPen = new Pen(Color.LightGray, backgroundLineWidth);
private bool drawBackground = true;
private List<Resistor> resistors = new List<Resistor>();
public Form1()
{
InitializeComponent();
}
private void Canvas_Paint(object sender, PaintEventArgs e)
{
if (drawBackground)
{
Console.WriteLine("Drawing background...");
Draw_Background(e.Graphics, backgroundPen);
}
if (resistors != null)
{
foreach (Resistor r in resistors)
{
//This does not work.
r.Draw(e.Graphics);
}
}
//The line below draws the line fine.
e.Graphics.DrawLine(pen, 0, 0, 100, 100);
}
private void Draw_Background(Graphics g, Pen pen)
{
for (int i = 0; i < Canvas.Width; i += 10)
{
g.DrawLine(pen, new Point(i, 0), new Point(i, Canvas.Height));
}
for (int j = 0; j < Canvas.Height; j += 10)
{
g.DrawLine(pen, new Point(0, j), new Point(Canvas.Width, j));
}
drawBackground = false;
}
private void AddResistor_Click(object sender, EventArgs e)
{
resistors.Add(new Resistor());
Console.WriteLine("Added a Resistor...");
}
}
Resistor Class:
public class Resistor
{
static private Point startingPoint;
static Pen defaultPen;
private Point[] points;
public Resistor()
{
startingPoint.X = 100;
startingPoint.Y = 100;
defaultPen = new Pen(Color.Yellow, 2.0F);
points = new Point[] {
new Point( 10, 10),
new Point( 10, 100),
new Point(200, 50),
new Point(250, 300)
};
}
public void Draw(Graphics g)
{
//Is this drawing somewhere else?
g.DrawLines(defaultPen, points);
}
}
I have looked at this question which suggests to pass the e.Graphics object in this case to the Draw method in the Resistor class but is not working.
I am new to C# so I would really appreciate any help.
EDIT :
I put the project on github if you want to download and try it out.
EDIT :
So the problem was that after clicking the button the panel Paint method was not being called. The solution was to add Canvas.Invalidate inside the AddResistor_Click method
Run your code in the debugger and put a breakpoint in your event handler and you'll be able to check that your code is actually trying to draw something. If not, then is your event handler ever called? is there anything in your list of resistors? If it is drawing but you don't see anything, then you're not using the correct Graphics context or you're not drawing things in the visible part of your control, or you're drawing over things with subsequent drawing code.
The problem was that when the button was clicked the panel's paint method was not getting called because I tough that the paint method was always getting called. The solution was to add Canvas.Invalidate inside the AddResistor_Click method.
private void AddResistor_Click(object sender, EventArgs e)
{
resistors.Add(new Resistor());
Console.WriteLine("Added a Resistor...");
Canvas.Invalidate();
}

Double buffering with C# has negative effect

I have written the following simple program, which draws lines on the screen every 100 milliseconds (triggered by timer1). I noticed that the drawing flickers a bit (that is, the window is not always completely blue, but some gray shines through). So my idea was to use double-buffering. But when I did that, it made things even worse. Now the screen was almost always gray, and only occasionally did the blue color come through (demonstrated by timer2, switching the DoubleBuffered property every 2000 milliseconds).
What could be an explanation for this?
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e) {
Graphics g = CreateGraphics();
Pen pen = new Pen(Color.Blue, 1.0f);
Random rnd = new Random();
for (int i = 0; i < Height; i++)
g.DrawLine(pen, 0, i, Width, i);
}
// every 100 ms
private void timer1_Tick(object sender, EventArgs e) {
Invalidate();
}
// every 2000 ms
private void timer2_Tick(object sender, EventArgs e) {
DoubleBuffered = !DoubleBuffered;
this.Text = DoubleBuffered ? "yes" : "no";
}
}
}
I would just draw all of your items to your own buffer, then copy it all in at once. I've used this for graphics in many applications, and it has always worked very well for me:
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
Invalidate();// every 100 ms
}
private void Form1_Load(object sender, EventArgs e)
{
DoubleBuffered = true;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Bitmap buffer = new Bitmap(Width, Height);
Graphics g = Graphics.FromImage(buffer);
Pen pen = new Pen(Color.Blue, 1.0f);
//Random rnd = new Random();
for (int i = 0; i < Height; i++)
g.DrawLine(pen, 0, i, Width, i);
BackgroundImage = buffer;
}
EDIT: After further investigation, it looks like your problem is what you're setting your Graphics object to:
Graphics g = CreateGraphics();
needs to be:
Graphics g = e.Graphics();
So your problem can be solved by either creating a manual buffer like I did above, or simply changing you Graphics object. I've tested both and they both work.
No need to use multiple buffers or Bitmap objects or anything.
Why don't you use the Graphics object provided by the Paint event? Like this:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Pen pen = new Pen(Color.Blue, 1.0f);
Random rnd = new Random();
for (int i = 0; i < Height; i++)
g.DrawLine(pen, 0, i, Width, i);
}
Try setting the double buffered property to true just once in the constructor while you're testing.
You need to make use of the back buffer. Try this:
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 DoubleBufferTest
{
public partial class Form1 : Form
{
private BufferedGraphicsContext context;
private BufferedGraphics grafx;
public Form1()
{
InitializeComponent();
this.Resize += new EventHandler(this.OnResize);
DoubleBuffered = true;
// Retrieves the BufferedGraphicsContext for the
// current application domain.
context = BufferedGraphicsManager.Current;
UpdateBuffer();
}
private void timer1_Tick(object sender, EventArgs e)
{
this.Refresh();
}
private void OnResize(object sender, EventArgs e)
{
UpdateBuffer();
this.Refresh();
}
private void UpdateBuffer()
{
// Sets the maximum size for the primary graphics buffer
// of the buffered graphics context for the application
// domain. Any allocation requests for a buffer larger
// than this will create a temporary buffered graphics
// context to host the graphics buffer.
context.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
// Allocates a graphics buffer the size of this form
// using the pixel format of the Graphics created by
// the Form.CreateGraphics() method, which returns a
// Graphics object that matches the pixel format of the form.
grafx = context.Allocate(this.CreateGraphics(),
new Rectangle(0, 0, this.Width, this.Height));
// Draw the first frame to the buffer.
DrawToBuffer(grafx.Graphics);
}
protected override void OnPaint(PaintEventArgs e)
{
grafx.Render(e.Graphics);
}
private void DrawToBuffer(Graphics g)
{
//Graphics g = grafx.Graphics;
Pen pen = new Pen(Color.Blue, 1.0f);
//Random rnd = new Random();
for (int i = 0; i < Height; i++)
g.DrawLine(pen, 0, i, Width, i);
}
}
}
It's a slightly hacked around version of a double buffering example on MSDN.

C# / Windows Forms: form's paint code not being executed

I posted a similar but less specific question a couple hours ago, but the circumstances have changed. I'm working on a program that transforms graphics, presenting them in a panel in the upper left of the form. It was painting fine earlier, but now isn't and I can't undo, load old version, etc. Previously, the app was unresponsive even to events in the menus, as well as painting. I started a new project and got it up and running, the menus work and I brought over the buttons and stuff. But it still won't paint the axes and gridlines in the panel. I put breakpoints in both the main forms's paint handler and the splitContainer2_Panel1_Paint handler that should be doing the work, but the code here isn't even being executed. I have a timer that's active and invalidating the whole form every 100 ms, so why would the paint event handler not be called? Help?
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;
using System.IO;
namespace TransformerA
{
public partial class Transformer : Form
{
/* Initialize parameters */
private bool drawAxes = true;
private bool drawGrid = true;
private List<ObjectSettings> dispObjects = new List<ObjectSettings>();
/* Initialize form */
public Transformer()
{
InitializeComponent();
}
private void Transformer_Load(object sender, EventArgs e)
{
// Populate available objects listbox
string currentDir = Directory.GetCurrentDirectory();
string[] fileEntries = Directory.GetFiles(currentDir + #"\Objects");
foreach (string s in fileEntries) {
int start = s.LastIndexOf(#"\");
int end = s.LastIndexOf(#".");
availObjectsListBox.Items.Add(s.Substring(start + 1, end - start - 1));
} // end foreach
}
/* Paint graphics */
// Paint main form
private void Transformer_Paint(object sender, PaintEventArgs e)
{
splitContainer2_Panel1_Paint(sender, e);
}
// Paint graphics panel
private void splitContainer2_Panel1_Paint(object sender, PaintEventArgs e)
{
Rectangle r = splitContainer2.Panel1.ClientRectangle;
//Graphics g = splitContainer2.Panel1.CreateGraphics();
Graphics g = e.Graphics;
Pen axisPen = new Pen(Color.Gray, 2.0f);
Pen gridPen = new Pen(Color.Gray, 1.0f);
g.Clear(Color.Blue);
if (drawAxes) {
g.DrawLine(axisPen, r.Left + 0.5f * r.Width, r.Top, r.Left + 0.5f * r.Width, r.Bottom);
g.DrawLine(axisPen, r.Left, r.Top + 0.5f * r.Height, r.Right, r.Top + 0.5f * r.Height);
}
if (drawGrid) {
// Vertical lines
int xVal = 0;
int xCenter = r.Width / 2;
g.DrawLine(gridPen, xCenter, r.Top, xCenter, r.Bottom);
for (int i = 0; i < 10; i++) {
xVal += r.Width / 20;
g.DrawLine(gridPen, xCenter + xVal, r.Top, xCenter + xVal, r.Bottom);
g.DrawLine(gridPen, xCenter - xVal, r.Top, xCenter - xVal, r.Bottom);
}
// Horizontal lines
int yVal = 0;
int yCenter = r.Height / 2;
g.DrawLine(gridPen, r.Left, yCenter, r.Right, yCenter);
for (int i = 0; i < 10; i++) {
yVal += r.Height / 20;
g.DrawLine(gridPen, r.Left, yCenter + yVal, r.Right, yCenter + yVal);
g.DrawLine(gridPen, r.Left, yCenter - yVal, r.Right, yCenter - yVal);
}
}
// foreach object in displayed objects
// keep list of displayed objects and their settings (make struct)
g.Dispose();
axisPen.Dispose();
gridPen.Dispose();
}
/* File menu */
private void saveImageToolStripMenuItem_Click(object sender, EventArgs e)
{
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
/* Options menu */
private void axesOnoffToolStripMenuItem_Click(object sender, EventArgs e)
{
if (drawAxes == true)
drawAxes = false;
else
drawAxes = true;
}
private void gridOnoffToolStripMenuItem_Click(object sender, EventArgs e)
{
if (drawGrid == true)
drawGrid = false;
else
drawGrid = true;
}
/* Help menu */
private void helpToolStripMenuItem_Click(object sender, EventArgs e)
{
AboutBox dlg = new AboutBox();
dlg.ShowDialog();
}
/* Other */
private void timer1_Tick(object sender, EventArgs e)
{
Invalidate();
}
}
}
Whoops, fixed it, I needed to add the event handler from the designer... i just pasted in the handler code from my old unworking version, so it didn't add the needed code to the designer code. Still can't figure out why it stopped working before but at least it's running now.

Categories