To use OnPaint
efficiently you have to know a couple things:
- The
OnPaint
of a control, e.g. of Form1
, is executed everytime the control is painted (duhh...)
- The
OnPaint
of Form1
is executed everytime a child control of Form1
is drawn. e.g. If you draw a dot in the upper right corner of Form1
using the OnPaint
of Form1
, while you have 150 child controls on Form1
, the dot will be drawn at least 150 times! It increases render time drastically. Especially if you do alot of custom drawing and calculations in the OnPaint
.
- So as a rule you must never have any logic in the
OnPaint
of a control, when that control has one or more child controls. Instead you should make a custom control, which holds no more child controls on it, that does the paint job. And place that as a child control on the parent control on the location where the custom drawing is needed.
- Whenever a control is added onto a parent, the parent will redraw. If you would place alot of controls on another control, e.g. a large result set with checkboxes on
Form1
, you must use Form1.SuspendLayout()
(see: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.suspendlayout.aspx) before you are adding the child controls. And Form1.ResumeLayout()
when you are done adding controls. This temporarily supresses the OnPaint
event, and decreases render time.
- Transparencies always increase render time.
- Placing components in such a way, that there is no background in between them, decreases the number of
OnPaint
events in the parent control. E.g. place 4 textboxes beneath eachother so that they thouch eachother. So there is no background in between them and the controls are all painted in one OnPaint
event instead of 4 OnPaint
events. Of course this is not always possible as you do not want to glue all your components side by side. But it is worth to do, if performance is more important than looks, for example in a large custom 'datagrid' of somekind.
- NEVER change a location or the size of a control in the
OnPaint
event, as this invokes new OnPaint
events. If you must relocate/resize controls, you will have to add that somewhere else in your code, before the OnPaint
is invoked. For example, place relocation/resizing code in the OnLayout
or OnResize
or similar events. If you still think you must place that relocation/resizing code in the OnPaint
event in order for you application to work, than you are wrong, and you need to revise the logic of your code.
- Think System.Math.Pow(2, 2) times before calling Refresh() on a control outside of its own class. If you have the urge to call Refresh you are probably in need of new events and event handlers to stay in sync with what you are willing to display. This is also the case for Invalidate().
- To check if you are drawing effenciently you can do the following. 1. Open you application 2. put a brake point on OnPaint in the Top Most parent 3. Maxamize a window so it covers your application. 4. Minimize the window again and you application will redraw control by control. If things are drawn double, you have made a mistake in the logic of you application.
Well I guess thats all, if something comes to mind that I forgot, i will update this Q&A. If I have forgotten something, or I made a mistake I would be glad to take note of it!
Hopefully this will give someone a headstart in using custom paint stuff in .Net, as I was looking for this information some time ago.