using GDI+ Graphics object and getting confused, help!

P

Peter Row

Hi,

I've started work on my own control some parts of which use standard
controls, others I need to draw on my controls surface to get the display
output I require, however....

I seem to be stupid or missing the point. I used DrawString( ) as a simple
test but I cannot get it to work at all unless I handle my custom controls
Paint event.

So for example if I say in the Paint event handler:
e.Graphics.DrawString("2006", this.Font, Brushes.Black, 50 , 2);

Then I see the text "2006" painted at coordinates 50, 2 on my control
as I'd expect.

HOWEVER....
If I simply have a method which says:

private void MyTestMethod( )
{
using (Graphics g = this.CreateGraphics( ))
{
g.DrawString("2006, this.Font, Brushes.Black, 50, 2);
}
}

I call the method and get absolutely nothing output, which is not what I
expect.
What the hell is going on?
What is the point of having a method on a control called CreateGraphics()
when you seemly can't use the object it returns to draw anything with it?

I am obviously missing something but I just can't see what that is, I looked
at a few examples of DrawString() on the net and they also seem to do it
within the confines of a Paint() event handler.

I probably could do what I need to do for my control in the Paint() event
handler but this is bugging me now and I have to know what I am doing
wrong or what I am misunderstanding.

Thanks for any help given,
Peter
 
N

Nicholas Paldino [.NET/C# MVP]

Peter,

In order to get custom painting to work, you have to handle the painting
in your Paint event handler (or override of the OnPaint method).

This is the case because of how windows programs work. Basically, you
have a loop which processes windows messages. Every so often, a message is
sent to the window saying "paint yourself".

So, your controls go and paint themselves.

Now, when you call your method and paint, you are painting, but if a
message comes in to the window to repaint itself, the device context (the
surface you are painting on) doesn't remember what was there. Also, because
you don't have the code in your paint method (event or OnPaint override), it
just paints itself, most likely clearing out whatever you did before.

So, this is why you have to place the code to paint in your paint
handler.

Now, what you are probably wondering is how do you handle changes in
what you want to display in your control. I'm sure you would rather just
call a method to do the painting, and then be done with it.

Unfortunately, because of the way the painting works, that's not
possible.

What you have to do is set variables/state that the paint method/handler
has access to, which will let that implementation of the method know how to
paint itself appropriately.

So this will set the state of what to show. For example, perhaps you
have changed text in a textbox and want it to be painted in your control
after you click a button. On the event handler for the button, you set a
string on the class level to the text in the textbox (or not at all, since
the textbox is on the same level, but it's the same concept).

At that point, you have to get your paint method/handler called.
Intuition tells you that you can just call it, but unfortunately, that will
just cause it to get wiped out again, since your graphics context will be
wiped out the next time your control needs redrawing (a process called
invalidation).

What you do at this point is tell windows that your window is
invalidated, and you need to be repainted. That is what the Invalidate
method is for on the Control class. Call that after you set your state, and
your paint handler/method will get called (with the right Graphics instance
to paint on, btw), and your changes will be persisted.

Hope this helps.
 
P

Peter Row

Hi,

Thanks for the reply, I knew about the whole Invalidate() method bit and
how that causes the Paint event to be fired.

However that still leaves one of my questions unanswered.
What is the point of the .CreateGraphics( ) method if you can't use the
Graphics object it returns to paint on your control?

I understand what you're saying about how by using it that it is actually
painting but it gets wiped out when the control gets told to paint itself.
But then as above what is the point of having that method at all.

It seems to be there as a tease. You could see where someone like myself
who is a software developer (MCP on exam 70-316, Win Apps in C#)
just I haven't done/needed to do any owner draw stuff before might get
misled. There is a method that creates a graphics object that references the
drawing surface of your control. So in my eyes it is not unreasonable to
expect that using the graphics object it gives to actually be able to see
some
visible results.

Again, thanks for the reply. I have started on a Paint event handler now
it's just annoying knowing something is there but can't be used.

Regards,
Peter

Nicholas Paldino said:
Peter,

In order to get custom painting to work, you have to handle the
painting in your Paint event handler (or override of the OnPaint method).

This is the case because of how windows programs work. Basically, you
have a loop which processes windows messages. Every so often, a message
is sent to the window saying "paint yourself".

So, your controls go and paint themselves.

Now, when you call your method and paint, you are painting, but if a
message comes in to the window to repaint itself, the device context (the
surface you are painting on) doesn't remember what was there. Also,
because you don't have the code in your paint method (event or OnPaint
override), it just paints itself, most likely clearing out whatever you
did before.

So, this is why you have to place the code to paint in your paint
handler.

Now, what you are probably wondering is how do you handle changes in
what you want to display in your control. I'm sure you would rather just
call a method to do the painting, and then be done with it.

Unfortunately, because of the way the painting works, that's not
possible.

What you have to do is set variables/state that the paint
method/handler has access to, which will let that implementation of the
method know how to paint itself appropriately.

So this will set the state of what to show. For example, perhaps you
have changed text in a textbox and want it to be painted in your control
after you click a button. On the event handler for the button, you set a
string on the class level to the text in the textbox (or not at all, since
the textbox is on the same level, but it's the same concept).

At that point, you have to get your paint method/handler called.
Intuition tells you that you can just call it, but unfortunately, that
will just cause it to get wiped out again, since your graphics context
will be wiped out the next time your control needs redrawing (a process
called invalidation).

What you do at this point is tell windows that your window is
invalidated, and you need to be repainted. That is what the Invalidate
method is for on the Control class. Call that after you set your state,
and your paint handler/method will get called (with the right Graphics
instance to paint on, btw), and your changes will be persisted.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)



Peter Row said:
Hi,

I've started work on my own control some parts of which use standard
controls, others I need to draw on my controls surface to get the display
output I require, however....

I seem to be stupid or missing the point. I used DrawString( ) as a
simple
test but I cannot get it to work at all unless I handle my custom
controls
Paint event.

So for example if I say in the Paint event handler:
e.Graphics.DrawString("2006", this.Font, Brushes.Black, 50 , 2);

Then I see the text "2006" painted at coordinates 50, 2 on my control
as I'd expect.

HOWEVER....
If I simply have a method which says:

private void MyTestMethod( )
{
using (Graphics g = this.CreateGraphics( ))
{
g.DrawString("2006, this.Font, Brushes.Black, 50, 2);
}
}

I call the method and get absolutely nothing output, which is not what I
expect.
What the hell is going on?
What is the point of having a method on a control called CreateGraphics()
when you seemly can't use the object it returns to draw anything with it?

I am obviously missing something but I just can't see what that is, I
looked
at a few examples of DrawString() on the net and they also seem to do it
within the confines of a Paint() event handler.

I probably could do what I need to do for my control in the Paint() event
handler but this is bugging me now and I have to know what I am doing
wrong or what I am misunderstanding.

Thanks for any help given,
Peter
 
R

rossum

Hi,

I've started work on my own control some parts of which use standard
controls, others I need to draw on my controls surface to get the display
output I require, however....

I seem to be stupid or missing the point. I used DrawString( ) as a simple
test but I cannot get it to work at all unless I handle my custom controls
Paint event.

So for example if I say in the Paint event handler:
e.Graphics.DrawString("2006", this.Font, Brushes.Black, 50 , 2);

Then I see the text "2006" painted at coordinates 50, 2 on my control
as I'd expect.

HOWEVER....
If I simply have a method which says:

private void MyTestMethod( )
{
using (Graphics g = this.CreateGraphics( ))
{
g.DrawString("2006, this.Font, Brushes.Black, 50, 2);
}
}

I call the method and get absolutely nothing output, which is not what I
expect.
What the hell is going on?
What is the point of having a method on a control called CreateGraphics()
when you seemly can't use the object it returns to draw anything with it?

I am obviously missing something but I just can't see what that is, I looked
at a few examples of DrawString() on the net and they also seem to do it
within the confines of a Paint() event handler.

I probably could do what I need to do for my control in the Paint() event
handler but this is bugging me now and I have to know what I am doing
wrong or what I am misunderstanding.

Thanks for any help given,
Peter

One way of working is to use a separate graphics buffer. Start by
copying the original of your control into the buffer and make all your
changes to the buffer. All you have to do in the Paint event is to
tell it to copy the buffer to the control. If you want something to
display immediately then just call myControl.Refresh() when you are
finished updating the buffer.

One advantage of this technique is that it can reduce screen flicker
when you are doing a lot of changes since changes to the buffer do not
appear on screen until they are copied to the control.

rossum
 
P

Peter Row

Hi,

How do I get access to "the buffer"?

Are you refering to a reference to a graphics object?

Wouldn't it be bad practice to keep a graphics object handing around
after your finished with it? Everything I've read says to dispose of it as
soon as you're done with it.

Regards,
Peter
 
B

Bob Powell [MVP]

Check out the information in the GDI+ FAQ.

Also, the beginners guide to GDI+ has some great info for you.
http://www.bobpowell.net/beginnersgdi.htm



--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.
 
R

rossum

Hi,

How do I get access to "the buffer"?
Are you refering to a reference to a graphics object?
It is a reference in the sense that is is not a value type, but it
does not refer to the Graphics object currently being displayed on the
screen. It starts as a cloned copy of that object and you then modify
it before copying it back to the control.
Wouldn't it be bad practice to keep a graphics object handing around
after your finished with it? Everything I've read says to dispose of it as
soon as you're done with it.
Yes, you only need it while you are drawing. If you are drawing at
multiple points in your application then you may need to keep it
around a bit longer but this will depend on the speed of response you
require, memory available and so on.

The buffer will need to be available to both the method(s) that draw
the graphics and to the MyControl_Paint().

rossum
Regards,
Peter
 
R

rossum

Hi,

How do I get access to "the buffer"?
Are you refering to a reference to a graphics object?
It is a reference in the sense that is is not a value type, but it
does not refer to the Graphics object currently being displayed on the
screen. It starts as a cloned copy of that object and you then modify
it before copying it back to the control.
Wouldn't it be bad practice to keep a graphics object handing around
after your finished with it? Everything I've read says to dispose of it as
soon as you're done with it.
Yes, you only need it while you are drawing. If you are drawing at
multiple points in your application then you may need to keep it
around a bit longer but this will depend on the speed of response you
require, memory available and so on.

The buffer will need to be available to both the method(s) that draw
the graphics and to the MyControl_Paint().

rossum
Regards,
Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top