views:

333

answers:

5

Normally when I override the OnPaint method, I create pens and brushes, etc inside it and then dispose them.

I also read somewhere that instead of recreating these pens and brushes, etc to create them once as static members, and then dispose them once when the form is closed, etc.

Is this a better practice?

Is there a better way for this?

I can assume that since OnPaint is called 1000s (?) of times, that would create a lot of work for the GC compared to creating them only once.

+4  A: 

If the brushes and pens don't change, it's certainly better to create them once and reuse them. Note, however, that if your control might be used on multiple threads (which is very unlikely), you should either make them ThreadStatic (and initialize on first use per thread) or make them instance members (and dispose them in your control's Dispose override); otherwise, you'll get unreproducable GDI+ errors, as GDI+ objects cannot be used on multiple threads at once. The same is true for images.

If the they do change (for example, if you use gradient brushes which depend on the control's size), you might still want to store them in instance fields, and recreate them when the control's size (or whatever) changes.

Note, by the way, that if you use normal colors, you can use the static Brushes and Pens classes, which contain static brushes and pens for all of .Net's built-in colors, and SystemBrushes and SystemPens for system colors.

SLaks
Thanks, one more question: when and how would you call the control's Dispose method? I never called it on the control before.
Joan Venge
You don't need to call it; .Net will call it when the parent control is disposed (When you dispose the form that it's on). If it's on your main form, it will be disposed at the end of the `Application.Run` call.
SLaks
Thanks Slaks. One more thing :) Since I generally use only 1 form, if I create another form that's not a main form, I have to dispose it after it's closed?
Joan Venge
Forms only automatically dispose when closed if they were shown with .Show or Application.Run (or equivalent). Forms showin with .ShowDialog are _not_ disposed when they are closed.
homeInAStar
@homeInAStar: Yes, you're right; I didn't read the method carefully enough. When calling `ShowDialog`, it's best to wrap the code in a `using` statement.
SLaks
+1  A: 

What I'd do is have the brushes and pens as members of the custom control, then dispose them when disposing of the control. This way you reuse the same brush/pen every time OnPaint is called.

I wouldn't declare them static though, because you wouldn't be able to know when you can dispose of your objects. But as SLaks mentioned, if there are many instances of the control in memory at the same time, it can be a good idea to create brushes and pens as static so that you have only one instance of each object created for the lifetime of your application.

Meta-Knight
If there will be many instances of the control, it would be a very good idea to make them static. There's nothing wrong with having a couple of brushes hanging around.
SLaks
That's a good point, if there are a lot of instances of this control, making the brushes and pens static would be a good idea.
Meta-Knight
A: 

A best practice would be to use system pens and brushes, as these are optimized for minimal resources consumption.

Sorin Comanescu
Only if he's using static built-in colors.
SLaks
+1  A: 

I read an excellent article last month which suggested doing all of your painting onto a separate BufferedGraphics object, and have the on_paint method do a straight copy from that to your control's graphics object.

That way, the on-paint is faster, and you only update your BufferedGraphics when something significant changes (i.e. a line moves or text changes).

Jonathan
+1  A: 

I really depends on what are you painting. If you will paint something that is repainted only as a result of an user interaction, you can loose your performance worries and create all of the graphic objects ad-hoc i.e. when needed.

Make sure that you Dispose() everything you need in your graphics. Pens, Brushes, Regions, Fonts. Those are all GDI objects and are tied into system by GDI handles.

If you need graphics that is animated in some way or will change in time without user clicking it, prepare all your graphic objects in front and reuse them as much as you can. Rule that can be applied here is that is better to waste memory then milliseconds in drawing every frame of the animation.

An lastly, at least for this post - don't forget to use double-buffering, either automatic in .net Control system or roll-your-own-back-bitmap style.

Have fun GDI-ing :)

Daniel Mošmondor