By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,903 Members | 1,410 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,903 IT Pros & Developers. It's quick & easy.

C# grotty graphics

P: n/a
If I look for guidance on this topic I find simple examples like this:

public class Form2:Form {
public Form2() {
this.Text = "FORM2";
this.Size = new Size(450,400);
this.Paint += new PaintEventHandler(Draw_Graphics);
}

public void Draw_Graphics(object sender,PaintEventArgs e)
{
Graphics g = e.Graphics;
System.Drawing.Bitmap bmap = new System.Drawing.Bitmap(500, 400);
for(int i = 0; i < 500; i++)
for(int j = 0; j < 50; j++)
bmap.SetPixel(i, j, Color.Red);
g.DrawImage(bmap,200,100);
}
};

static void Main()
{
Application.Run(new Form2());
}

This works as advertised but there is some sly sleight of hand going on
here.
Apparently the Form2 constructor raises a signal that is caught by the
handler Draw_Graphics().
What was the signal and who raised it?
I ask because if I replace the innocuous-looking statement
"Application.Run" by a simple call to the constructor it executes that but
no event is raised and the handler does not run.
What is the dark secret here? What does Application.Run() actually do?

Now I want to run a dialog box first and then perform the above, so I say:

Application.Run(new Form1())
Application.Exit();
Application.Run(new Form2())

No dice! I get Form1 and the Form2 constructor but no signal.

Ray Reeves


Mar 3 '07 #1
Share this Question
Share on Google+
7 Replies


P: n/a
<"rayreeves" <ray<mond>re****@verizon.net>wrote:
If I look for guidance on this topic I find simple examples like this:

public class Form2:Form {
public Form2() {
this.Text = "FORM2";
this.Size = new Size(450,400);
this.Paint += new PaintEventHandler(Draw_Graphics);
}

public void Draw_Graphics(object sender,PaintEventArgs e)
{
Graphics g = e.Graphics;
System.Drawing.Bitmap bmap = new System.Drawing.Bitmap(500, 400);
for(int i = 0; i < 500; i++)
for(int j = 0; j < 50; j++)
bmap.SetPixel(i, j, Color.Red);
g.DrawImage(bmap,200,100);
}
};

static void Main()
{
Application.Run(new Form2());
}

This works as advertised but there is some sly sleight of hand going on
here.
Apparently the Form2 constructor raises a signal that is caught by the
handler Draw_Graphics().
No, the Windows message loop requests that the form is painted, and the
constructor to Form2 registers a handler for the paint event.
What was the signal and who raised it?
I ask because if I replace the innocuous-looking statement
"Application.Run" by a simple call to the constructor it executes that but
no event is raised and the handler does not run.
What is the dark secret here? What does Application.Run() actually do?
It starts a Windows message loop.
Now I want to run a dialog box first and then perform the above, so I say:

Application.Run(new Form1())
Application.Exit();
Application.Run(new Form2())

No dice! I get Form1 and the Form2 constructor but no signal.
Well, you don't want Application.Exit() in there - that will exit the
application (as the name suggests) unless you have any other threads
running.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Mar 3 '07 #2

P: n/a
"rayreeves" <ray<mond>re****@verizon.netwrote:
If I look for guidance on this topic I find simple examples like this:

public class Form2:Form {
public Form2() {
this.Text = "FORM2";
this.Size = new Size(450,400);
this.Paint += new PaintEventHandler(Draw_Graphics);
}

public void Draw_Graphics(object sender,PaintEventArgs e)
{
Graphics g = e.Graphics;
System.Drawing.Bitmap bmap = new System.Drawing.Bitmap(500, 400);
for(int i = 0; i < 500; i++)
for(int j = 0; j < 50; j++)
bmap.SetPixel(i, j, Color.Red);
g.DrawImage(bmap,200,100);
}
};

static void Main()
{
Application.Run(new Form2());
}

This works as advertised but there is some sly sleight of hand going on
here.
Yes. It's Win32's windowing system.
Apparently the Form2 constructor raises a signal that is caught by the
handler Draw_Graphics().
No. Application.Run() contains a loop (called the "message loop" or
"message pump") which does basically two things in its body:

1) Retrieve a message from Windows (see e.g. GetMessage and PeekMessage
in MSDN docs)
2) Dispatch that message from Windows (see e.g. DispatchMessage in MSDN
docs)

Messages are things like WM_PAINT, which is a request to paint a window
(for example, the window might have been obscured by another window, and
is now visible). Messages contain several parameters: the message code
(WM_PAINT in this case), the window it applies to, and two parameters
whose meaning changes based on message kind (wParam and lParam).

Retrieving the message and dispatching the message are done by the
application so that it happens on a thread the application controls.

Dispatching the message sends it off to something called a window
procedure, which is a function pointer (a bit like a delegate)
associated with that window in the OS. When the window is first created,
the creator of the window gets a chance to specify this window procedure
(see CreateWindow in MSDN docs).

The window procedure for .NET controls is Control.WndProc. Basically,
different descendants of Control have 'case' statements that switch on
the message type and call different methods based on the message.

In Control.WndProc, there's an entry in this 'case' statement that calls
a private method called WmPaint, which in turn calls
PaintWithErrorHandling, which itself calls OnPaint, which finally calls
the 'Paint' event if it is non-null.

I'm leaving out many details, but that's the gist of it.
Now I want to run a dialog box first and then perform the above, so I say:

Application.Run(new Form1())
This sets up a message loop and keeps going until the application exits
(with Application.Exit) or the main form (the argument) is closed.
Application.Exit();
Because there's no loop to exit, this does nothing. The documentation
for this method says:

"Informs all message pumps that they must terminate, and then closes all
application windows after the messages have been processed."

Because there's no message pump active (Application.Run() is not
running), and there's no forms on screen at this point, the method won't
do anything.
Application.Run(new Form2())
This shows a second form, in a new loop.

-- Barry

--
http://barrkel.blogspot.com/
Mar 3 '07 #3

P: n/a
Jon Skeet [C# MVP] <sk***@pobox.comwrote:
Well, you don't want Application.Exit() in there - that will exit the
application (as the name suggests) unless you have any other threads
running.
Doh! Ignore this bit, and read Barry's answer instead, which is much
more sensible.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Mar 3 '07 #4

P: n/a

"Barry Kelly" <ba***********@gmail.comwrote in message
news:e8********************************@4ax.com...
"
>Application.Run(new Form2())

This shows a second form, in a new loop.

-- Barry

--
http://barrkel.blogspot.com/
Thanks for the comprehensive and detailed response.
Unfortunately, the second form Form2 does not appear, unless it is the only
form. It would be nice to know if a Paint event actually occurred and who
caught it.

I don't even really want to paint on a Form but it's not apparent to me that
I can just create a window. I would also like to SetPixel directly in the
window without using BitMaps so that I can watch the picture grow, but that
doesn't seem possible either.

Ray Reeves
Mar 4 '07 #5

P: n/a
"rayreeves" <ray<mond>re****@verizon.netwrote:
Thanks for the comprehensive and detailed response.
Unfortunately, the second form Form2 does not appear, unless it is the only
form. It would be nice to know if a Paint event actually occurred and who
caught it.
The second form should appear after you close the first. It does in this
simple test program:

---8<---
using System;
using System.Windows.Forms;

class App
{
public static void Main()
{
Application.Run(new Form());
Application.Exit();
Application.Run(new Form());
}
}
--->8---
I don't even really want to paint on a Form but it's not apparent to me that
I can just create a window. I would also like to SetPixel directly in the
window without using BitMaps so that I can watch the picture grow, but that
doesn't seem possible either.
Windows doesn't use a "retained mode" graphics infrastructure.
Everything is lost if the window is obscured in any way. You need to
draw to a bitmap, and keep the bitmap around. For example:

---8<---
using System;
using System.Drawing;
using System.Windows.Forms;

class MyForm : Form
{
Bitmap _image = new Bitmap(500, 500);
Button _testButton = new Button();
Random r = new Random();

public MyForm()
{
Paint += MyPaintEvent;
using (Graphics g = Graphics.FromImage(_image))
g.FillRectangle(Brushes.White, 0, 0, 500, 500);
ClientSize = new Size(500, 500);
_testButton.Parent = this;
_testButton.Text = "Click Here";
_testButton.Click += MyClickEvent;
DoubleBuffered = true;
}

void MyClickEvent(object sender, EventArgs e)
{
// Random color for RGB fields, and 'OR' in 255 for Alpha.
Color c = Color.FromArgb(r.Next(0x1000000) | (0xFF << 24));
using (Graphics g = Graphics.FromImage(_image))
{
using (Pen p = new Pen(c))
g.DrawLine(p, r.Next(500), r.Next(500),
r.Next(500), r.Next(500));
}
_image.SetPixel(r.Next(500), r.Next(500), c);
// Invalidate() needed to get windows to send paint message
// to repaint form.
Invalidate();
}

void MyPaintEvent(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_image, 0, 0);
}

public static void Main()
{
Application.Run(new MyForm());
}
}
--->8---

-- Barry

--
http://barrkel.blogspot.com/
Mar 4 '07 #6

P: n/a
I do exactly that in my code but don't succeed. Somehow my Dialog Form is
intercepting the Paint event, if there is one. Its true that a simpler Form1
does work properly for me..I'll have to try whitteling down my Dialog bit by
bit.
I am very much obliged for your kind attention and detailed help.

Ray Reeves

"Barry Kelly" <ba***********@gmail.comwrote in message
news:ea********************************@4ax.com...
"rayreeves" <ray<mond>re****@verizon.netwrote:
>Thanks for the comprehensive and detailed response.
Unfortunately, the second form Form2 does not appear, unless it is the
only
form. It would be nice to know if a Paint event actually occurred and who
caught it.

The second form should appear after you close the first. It does in this
simple test program:

---8<---
using System;
using System.Windows.Forms;

class App
{
public static void Main()
{
Application.Run(new Form());
Application.Exit();
Application.Run(new Form());
}
}
--->8---
>I don't even really want to paint on a Form but it's not apparent to me
that
I can just create a window. I would also like to SetPixel directly in the
window without using BitMaps so that I can watch the picture grow, but
that
doesn't seem possible either.

Windows doesn't use a "retained mode" graphics infrastructure.
Everything is lost if the window is obscured in any way. You need to
draw to a bitmap, and keep the bitmap around. For example:

---8<---
using System;
using System.Drawing;
using System.Windows.Forms;

class MyForm : Form
{
Bitmap _image = new Bitmap(500, 500);
Button _testButton = new Button();
Random r = new Random();

public MyForm()
{
Paint += MyPaintEvent;
using (Graphics g = Graphics.FromImage(_image))
g.FillRectangle(Brushes.White, 0, 0, 500, 500);
ClientSize = new Size(500, 500);
_testButton.Parent = this;
_testButton.Text = "Click Here";
_testButton.Click += MyClickEvent;
DoubleBuffered = true;
}

void MyClickEvent(object sender, EventArgs e)
{
// Random color for RGB fields, and 'OR' in 255 for Alpha.
Color c = Color.FromArgb(r.Next(0x1000000) | (0xFF << 24));
using (Graphics g = Graphics.FromImage(_image))
{
using (Pen p = new Pen(c))
g.DrawLine(p, r.Next(500), r.Next(500),
r.Next(500), r.Next(500));
}
_image.SetPixel(r.Next(500), r.Next(500), c);
// Invalidate() needed to get windows to send paint message
// to repaint form.
Invalidate();
}

void MyPaintEvent(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_image, 0, 0);
}

public static void Main()
{
Application.Run(new MyForm());
}
}
--->8---

-- Barry

--
http://barrkel.blogspot.com/

Mar 4 '07 #7

P: n/a
What I have found is that if the Form1 Dialog Box contains plain buttons
then when they are pressed, even if they do nothing, the Paint signal to
Form2 is pre-empted! Closing the Form1 window is the only way to get to
Form2.
Grotty!

Ray Reeves
Mar 6 '07 #8

This discussion thread is closed

Replies have been disabled for this discussion.