Drawing onto a Windows Form without freezing it
-
Hi, I would like to draw onto a Windows form constantly, without freezing it. I know it is possible, but how? :) Thanks in advance.
-
Hi, I would like to draw onto a Windows form constantly, without freezing it. I know it is possible, but how? :) Thanks in advance.
Application.DoEvents()
is probably what you're after. What sort of drawing are you doing continuously?Regards, Rob Philpott.
-
Hi, I would like to draw onto a Windows form constantly, without freezing it. I know it is possible, but how? :) Thanks in advance.
Create timer and every tick event is fired call
Invalidate()
method -
Application.DoEvents()
is probably what you're after. What sort of drawing are you doing continuously?Regards, Rob Philpott.
I've thought about
Application.DoEvents()
but some say that it is not a safe solution. I think I can summarize what I am doing as creating random bitmaps until it is identical to another bitmap. I know the concept is meaningless, but it is just a test which is leading to familiarizing myself with the idea of drawing onto Windows forms. :) -
Create timer and every tick event is fired call
Invalidate()
methodI've tried it but the form is still freezing and I've no control over it. Here is the code I have in my OnPaint method.
protected override void OnPaint(PaintEventArgs e)
{
if (session == 0)
{
Graphics dc = e.Graphics;Pen CyanPen = new Pen(Color.Cyan, 1); Pen MagentaPen = new Pen(Color.Magenta, 1); Pen YellowPen = new Pen(Color.Yellow, 1); Pen BlackPen = new Pen(Color.Black, 1); Random rnd = new Random(); int buff = 0; for (int y = 0; y < 300; y++) { for (int x = 0; x < 300; x++) { buff = rnd.Next(0, 4); if (buff == 0) dc.DrawRectangle(CyanPen, x, y, 1, 1); if (buff == 1) dc.DrawRectangle(MagentaPen, x, y, 1, 1); if (buff == 2) dc.DrawRectangle(YellowPen, x, y, 1, 1); if (buff == 3) dc.DrawRectangle(BlackPen, x, y, 1, 1); } } session++; } }
-
I've tried it but the form is still freezing and I've no control over it. Here is the code I have in my OnPaint method.
protected override void OnPaint(PaintEventArgs e)
{
if (session == 0)
{
Graphics dc = e.Graphics;Pen CyanPen = new Pen(Color.Cyan, 1); Pen MagentaPen = new Pen(Color.Magenta, 1); Pen YellowPen = new Pen(Color.Yellow, 1); Pen BlackPen = new Pen(Color.Black, 1); Random rnd = new Random(); int buff = 0; for (int y = 0; y < 300; y++) { for (int x = 0; x < 300; x++) { buff = rnd.Next(0, 4); if (buff == 0) dc.DrawRectangle(CyanPen, x, y, 1, 1); if (buff == 1) dc.DrawRectangle(MagentaPen, x, y, 1, 1); if (buff == 2) dc.DrawRectangle(YellowPen, x, y, 1, 1); if (buff == 3) dc.DrawRectangle(BlackPen, x, y, 1, 1); } } session++; } }
-
That's a lot of drawing 300 by 300 => 90000 rectagles in one session :omg: Try putting a a this.Invalidate inside the first for and out of the second/inner for.:~
I tried your advise and still, I can't do anything on the form until the drawing is over.
-
I've tried it but the form is still freezing and I've no control over it. Here is the code I have in my OnPaint method.
protected override void OnPaint(PaintEventArgs e)
{
if (session == 0)
{
Graphics dc = e.Graphics;Pen CyanPen = new Pen(Color.Cyan, 1); Pen MagentaPen = new Pen(Color.Magenta, 1); Pen YellowPen = new Pen(Color.Yellow, 1); Pen BlackPen = new Pen(Color.Black, 1); Random rnd = new Random(); int buff = 0; for (int y = 0; y < 300; y++) { for (int x = 0; x < 300; x++) { buff = rnd.Next(0, 4); if (buff == 0) dc.DrawRectangle(CyanPen, x, y, 1, 1); if (buff == 1) dc.DrawRectangle(MagentaPen, x, y, 1, 1); if (buff == 2) dc.DrawRectangle(YellowPen, x, y, 1, 1); if (buff == 3) dc.DrawRectangle(BlackPen, x, y, 1, 1); } } session++; } }
Of course it keep freezing. In orther to respond, you need to make sure it only redraws once. And use timer.Tick event and forcfully call OnPaint by calling Invalidate(). Do not call OnPaint() directly. Invalidate works like a message pump (Or at least i think so). If it is called 3 times and redraw hasn't occourt, it will only redraw once, and clear quee. other option is to put onto another thread. But i do not recomend. I would recomend a timer. ps: Make sure to call dispose on a PEN, to clear unmanaged resources. I would recomend you use only this code inside OnPaint. And make another Thread like a timer to Invalidate(), or OnPaint (Forcfully) as long as it doesn't call too frequent.
if (buff == 0)
dc.DrawRectangle(CyanPen, x, y, 1, 1);
if (buff == 1)
dc.DrawRectangle(MagentaPen, x, y, 1, 1);
if (buff == 2)
dc.DrawRectangle(YellowPen, x, y, 1, 1);
if (buff == 3)
dc.DrawRectangle(BlackPen, x, y, 1, 1); -
I tried your advise and still, I can't do anything on the form until the drawing is over.
-
I just realized that my first reply or message is a total ... My bad. All of the drawing is inside the OnPaint()!!!! Use the fors outside the OnPaint() or better said call the paint or invalidate method FROM the fors.
I see. But there is some thing that I don't understand. If I use OnPaint method, only for the painting process while randomizing in another method; how am I going to transfer my variables like color and position to the OnPaint method? I can use global variables but is it a good solution? There should be an easier and neater solution.
-
I see. But there is some thing that I don't understand. If I use OnPaint method, only for the painting process while randomizing in another method; how am I going to transfer my variables like color and position to the OnPaint method? I can use global variables but is it a good solution? There should be an easier and neater solution.
-
Of course it keep freezing. In orther to respond, you need to make sure it only redraws once. And use timer.Tick event and forcfully call OnPaint by calling Invalidate(). Do not call OnPaint() directly. Invalidate works like a message pump (Or at least i think so). If it is called 3 times and redraw hasn't occourt, it will only redraw once, and clear quee. other option is to put onto another thread. But i do not recomend. I would recomend a timer. ps: Make sure to call dispose on a PEN, to clear unmanaged resources. I would recomend you use only this code inside OnPaint. And make another Thread like a timer to Invalidate(), or OnPaint (Forcfully) as long as it doesn't call too frequent.
if (buff == 0)
dc.DrawRectangle(CyanPen, x, y, 1, 1);
if (buff == 1)
dc.DrawRectangle(MagentaPen, x, y, 1, 1);
if (buff == 2)
dc.DrawRectangle(YellowPen, x, y, 1, 1);
if (buff == 3)
dc.DrawRectangle(BlackPen, x, y, 1, 1);This is my current code and I still have the freezing issue but I think we are getting there. :)
private void SetPixel(Point xy, Color clr)
{
Graphics dc = this.CreateGraphics();
Pen _pen = new Pen(clr, 1);dc.DrawRectangle(\_pen, new Rectangle( xy, new Size(1,1) ) ); \_pen.Dispose(); dc.Dispose(); } private void button1\_Click\_1(object sender, EventArgs e) { Random rnd = new Random(); int buff = 0; for (int y = 0; y < 300; y++) { this.Invalidate(); for (int x = 0; x < 300; x++) { this.Invalidate(); buff = rnd.Next(0, 4); if (buff == 0) SetPixel(new Point(x,y),Color.Cyan); else if (buff == 1) SetPixel(new Point(x,y),Color.Magenta); else if (buff == 2) SetPixel(new Point(x,y),Color.Yellow); else if (buff == 3) SetPixel(new Point(x,y),Color.Black); } } }
-
This is my current code and I still have the freezing issue but I think we are getting there. :)
private void SetPixel(Point xy, Color clr)
{
Graphics dc = this.CreateGraphics();
Pen _pen = new Pen(clr, 1);dc.DrawRectangle(\_pen, new Rectangle( xy, new Size(1,1) ) ); \_pen.Dispose(); dc.Dispose(); } private void button1\_Click\_1(object sender, EventArgs e) { Random rnd = new Random(); int buff = 0; for (int y = 0; y < 300; y++) { this.Invalidate(); for (int x = 0; x < 300; x++) { this.Invalidate(); buff = rnd.Next(0, 4); if (buff == 0) SetPixel(new Point(x,y),Color.Cyan); else if (buff == 1) SetPixel(new Point(x,y),Color.Magenta); else if (buff == 2) SetPixel(new Point(x,y),Color.Yellow); else if (buff == 3) SetPixel(new Point(x,y),Color.Black); } } }
i would suggest what you have in button method you move to other thread. and it is incorect usage. you must let all drawing into OnPaint for perforamce. Invalidate prepares a message into a quee, when message pump has avaible slot, it will call OnPaint. Your Invalidate() is meanigless.
-
i would suggest what you have in button method you move to other thread. and it is incorect usage. you must let all drawing into OnPaint for perforamce. Invalidate prepares a message into a quee, when message pump has avaible slot, it will call OnPaint. Your Invalidate() is meanigless.
I now got it working. It is the incorrect usage but whatever, it seems to be working. The only problem I've left is the issue when the windows gets out of sight, the drawings I did are gone. How can I solve this?
private void SetPixel(Point xy, Color clr)
{
Graphics dc = this.CreateGraphics();
Pen _pen = new Pen(clr, 1);dc.DrawRectangle(_pen, new Rectangle( xy, new Size(1,1) ) );
_pen.Dispose(); dc.Dispose();
}private void button1_Click_1(object sender, EventArgs e)
{
Thread th = new Thread( new ThreadStart(Dummy) );
th.Name = "Drawer";
th.Start();
}private void Dummy()
{
Random rnd = new Random();
int buff = 0;for (int y = 0; y < 300; y++)
{
for (int x = 0; x < 300; x++)
{
buff = rnd.Next(0, 4);
if (buff == 0)
SetPixel(new Point(x,y),Color.Cyan);
else if (buff == 1)
SetPixel(new Point(x,y),Color.Magenta);
else if (buff == 2)
SetPixel(new Point(x,y),Color.Yellow);
else if (buff == 3)
SetPixel(new Point(x,y),Color.Black);
}
}
} -
I see. But there is some thing that I don't understand. If I use OnPaint method, only for the painting process while randomizing in another method; how am I going to transfer my variables like color and position to the OnPaint method? I can use global variables but is it a good solution? There should be an easier and neater solution.
SimpleData wrote:
use global variables
use class members to do so, and make sure your Paint handler executes fast. I suggest you read this[^] little article of mine. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles]
I only read code that is properly formatted, adding PRE tags is the easiest way to obtain that.
All Toronto weekends should be extremely wet until we get it automated in regular forums, not just QA.
modified on Friday, February 26, 2010 12:58 PM
-
This is my current code and I still have the freezing issue but I think we are getting there. :)
private void SetPixel(Point xy, Color clr)
{
Graphics dc = this.CreateGraphics();
Pen _pen = new Pen(clr, 1);dc.DrawRectangle(\_pen, new Rectangle( xy, new Size(1,1) ) ); \_pen.Dispose(); dc.Dispose(); } private void button1\_Click\_1(object sender, EventArgs e) { Random rnd = new Random(); int buff = 0; for (int y = 0; y < 300; y++) { this.Invalidate(); for (int x = 0; x < 300; x++) { this.Invalidate(); buff = rnd.Next(0, 4); if (buff == 0) SetPixel(new Point(x,y),Color.Cyan); else if (buff == 1) SetPixel(new Point(x,y),Color.Magenta); else if (buff == 2) SetPixel(new Point(x,y),Color.Yellow); else if (buff == 3) SetPixel(new Point(x,y),Color.Black); } } }
CreateGraphics() is an expensive method (expect everything with Create in its name to be expensive and slow); you should really do without it. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles]
I only read code that is properly formatted, adding PRE tags is the easiest way to obtain that.
All Toronto weekends should be extremely wet until we get it automated in regular forums, not just QA.
-
I now got it working. It is the incorrect usage but whatever, it seems to be working. The only problem I've left is the issue when the windows gets out of sight, the drawings I did are gone. How can I solve this?
private void SetPixel(Point xy, Color clr)
{
Graphics dc = this.CreateGraphics();
Pen _pen = new Pen(clr, 1);dc.DrawRectangle(_pen, new Rectangle( xy, new Size(1,1) ) );
_pen.Dispose(); dc.Dispose();
}private void button1_Click_1(object sender, EventArgs e)
{
Thread th = new Thread( new ThreadStart(Dummy) );
th.Name = "Drawer";
th.Start();
}private void Dummy()
{
Random rnd = new Random();
int buff = 0;for (int y = 0; y < 300; y++)
{
for (int x = 0; x < 300; x++)
{
buff = rnd.Next(0, 4);
if (buff == 0)
SetPixel(new Point(x,y),Color.Cyan);
else if (buff == 1)
SetPixel(new Point(x,y),Color.Magenta);
else if (buff == 2)
SetPixel(new Point(x,y),Color.Yellow);
else if (buff == 3)
SetPixel(new Point(x,y),Color.Black);
}
}
}I have done it:
private Bitmap bit;
private Graphics dc;
// Used to suspend layout
private delegate void Suspend(bool b);
private Suspend mySuspend;private void SuspendLayoutDel(bool b)
{
if (b)
this.SuspendLayout();
else
{
this.ResumeLayout();
// Set to true to force full redraw
Invalidate(true);
}
}private void SetPixel(Point xy, Color clr, Graphics dc)
{
Pen _pen = new Pen(clr, 1);//Lock prevents accsess from other threads lock(dc) { dc.DrawRectangle(\_pen, new Rectangle(xy, new Size(1, 1))); } \_pen.Dispose();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (bit == null)
{
bit = new Bitmap(300, 300);
dc = Graphics.FromImage(bit);
}
e.Graphics.DrawImage(bit,0,0);
}private void button1_Click(object sender, EventArgs e)
{
// Asign delegate so that Invoke is useed (Threat Safety)
mySuspend = new Suspend(SuspendLayoutDel);
Thread th = new Thread(new ThreadStart(Dummy));
th.Name = "Drawer";
th.Start();
}private void Dummy()
{
this.Invoke(mySuspend, new Object[]{true});
Random rnd = new Random();
int buff = 0;for (int y = 0; y < 300; y++) { for (int x = 0; x < 300; x++) { buff = rnd.Next(0, 4); if (buff == 0) SetPixel(new Point(x, y), Color.Cyan,dc); else if (buff == 1) SetPixel(new Point(x, y), Color.Magenta, dc); else if (buff == 2) SetPixel(new Point(x, y), Color.Yellow, dc); else if (buff == 3) SetPixel(new Point(x, y), Color.Black, dc); } } this.Invoke(mySuspend, new Object\[\] { false });
}
I used graphic to draw to bitmap. Aka to memory. After all is displayed, it draws on screen. This aproach is olmost instantly compared to draw directly to screen.