WinForms painting - SuspendLayout, dblBuffer etc
from:
http://discuss.joelonsoftware.com/default.asp?dotnet.12.454272.8
Hello,
(Some context... I work on moderately complex WinForms applications, eg many screens, with complex user controls that compose infragistics with common controls etc, plus a fair bit of background processing (mostly via BackgroundWorkers). I don't use any true custom controls with fully manual painting.)
I routinely run into problems with rendering, particularly:
1) slow rendering
2) rendering process is visible to user.
3) flicker during appForm resize and other situations.
My main issue is that I haven't found a way to fully suspend drawing on a Form or User control that will halt all drawing/layout events on all user-controls and child controls?
DoubleBuffering - seems to only affect the one control and doesn't help with the rendering of children.
SuspendLayout - doesn't halt painting, only the Layout.
Any suggestions appreciated.
Play with the SetStyle setting on various controls ( especially DoubleBuffering, AllPaintingInWmPaint and UserPaint ( don't know how well UserPaint will work for you ).
Play with these settings on your various controls and see how that works.
Play with these settings on your various controls and see how that works.
Wednesday, February 21, 2007
Here is a specific scenario to consider
The application form has a wizard-like container control that can host multiple pages. Each page is a rich UserControl with complex and nested child controls.
The wizard-container is showing the first page (visible, dock-fill), and the second-page is hidden (visible=false, enabled=false, and/or not even in the controls list).
=> How would you transition from page1 to page2 to acheive slick rendering. In particular, the nicest result is probably a short pause (no visual change to page1) while work is done, then 'pop' the new screen is fully rendered.
=> I find this scenario very hard to get right in general and every application that does this sort of thing has to be custom-fiddled to get a nice result. I often test variations of orderings of Update/Refresh/BringToFront/Hide/Show and the results are often very suprising (eg change one minor ordering and the visual outcome is drastically different). Sometimes I try manual rendering of the entire control (including all child controls) to back-buffer, but I haven't had any success with that so far.
How do other people with this scenario?
Any suggestions appreciated.
The application form has a wizard-like container control that can host multiple pages. Each page is a rich UserControl with complex and nested child controls.
The wizard-container is showing the first page (visible, dock-fill), and the second-page is hidden (visible=false, enabled=false, and/or not even in the controls list).
=> How would you transition from page1 to page2 to acheive slick rendering. In particular, the nicest result is probably a short pause (no visual change to page1) while work is done, then 'pop' the new screen is fully rendered.
=> I find this scenario very hard to get right in general and every application that does this sort of thing has to be custom-fiddled to get a nice result. I often test variations of orderings of Update/Refresh/BringToFront/Hide/Show and the results are often very suprising (eg change one minor ordering and the visual outcome is drastically different). Sometimes I try manual rendering of the entire control (including all child controls) to back-buffer, but I haven't had any success with that so far.
How do other people with this scenario?
Any suggestions appreciated.
> Sometimes I try manual rendering of the entire control (including all child controls) to back-buffer, but I haven't had any success with that so far.
I don't know what you've done and haven't tried anything like this myself, but here's a guess.
When a control isn't visible (e.g. when it's hidden behind another) then the standard OnPaint doesn't work, because the associated Graphics region (which paints to the screen) is clipped.
What you might be wanting to try, therefore, is draw to a non-screen Graphics object, which you can then blit to the screen.
The example shown in the help for the BufferedGraphics class might help you do that.
I don't know how to use BufferedGraphics to buffer output from standard controls (I can see how to do it from custom controls). Maybe it means implementing a MyRender method in your form, which constructs a PaintEventArgs using a Graphics instance obtained from a BufferedGraphics instance, and which invokes the OnPaint method of the various controls in the form (passing your own PaintEventArgs instance to the controls' OnPaint methods).
FWIW, printing too entails drawing to a non-screen buffer; I've never implemented printing so I don't know whether that works with standard controls (e.g. whether printing persuades them to render to a non-screen Graphics object).
The above is something along the lines of what you've been trying, but beware that I'm not confident that this is the best or standard way to do it.
Also I don't know about "BackgroundWorkers": does that slow down rendering? A tiny bit of flicker or shimmer is normal (and so, expected and barely noticeable) at the best of times when a screen is refreshed (e.g when you scroll a multi-line edit box); but not a lot of flicker.
I don't know what you've done and haven't tried anything like this myself, but here's a guess.
When a control isn't visible (e.g. when it's hidden behind another) then the standard OnPaint doesn't work, because the associated Graphics region (which paints to the screen) is clipped.
What you might be wanting to try, therefore, is draw to a non-screen Graphics object, which you can then blit to the screen.
The example shown in the help for the BufferedGraphics class might help you do that.
I don't know how to use BufferedGraphics to buffer output from standard controls (I can see how to do it from custom controls). Maybe it means implementing a MyRender method in your form, which constructs a PaintEventArgs using a Graphics instance obtained from a BufferedGraphics instance, and which invokes the OnPaint method of the various controls in the form (passing your own PaintEventArgs instance to the controls' OnPaint methods).
FWIW, printing too entails drawing to a non-screen buffer; I've never implemented printing so I don't know whether that works with standard controls (e.g. whether printing persuades them to render to a non-screen Graphics object).
The above is something along the lines of what you've been trying, but beware that I'm not confident that this is the best or standard way to do it.
Also I don't know about "BackgroundWorkers": does that slow down rendering? A tiny bit of flicker or shimmer is normal (and so, expected and barely noticeable) at the best of times when a screen is refreshed (e.g when you scroll a multi-line edit box); but not a lot of flicker.
What I wound up doing was to create a user control that 'drew' the child controls as graphics images (bitmaps, lines, rectangles, etc). Then one of two approaches:
1. Place the 'real' controls (one at a time) in the normal tab order as the user tranverses the image, redrawing that control to show any changes, or put the 'real' control where the user clicked if not folloing the tab order.
2. In the situation where only one record would be live, pop-up a dialgo witht ht real controls and redrraw the image when the dialgo was dismissed, showing the changed data.
1. Place the 'real' controls (one at a time) in the normal tab order as the user tranverses the image, redrawing that control to show any changes, or put the 'real' control where the user clicked if not folloing the tab order.
2. In the situation where only one record would be live, pop-up a dialgo witht ht real controls and redrraw the image when the dialgo was dismissed, showing the changed data.
stoneyowl
Thursday, February 22, 2007
Thursday, February 22, 2007
Some old fashioned Win32 code might help, if you know when you want to turn updates off and then on:
public void BeginUpdate()
{
SendMessage( this.Handle, WM_SETREDRAW, 0, 0);
}
public void EndUpdate()
{
SendMessage( this.Handle, WM_SETREDRAW, 1, 0);
Parent.Invalidate(true);
}
public void BeginUpdate()
{
SendMessage( this.Handle, WM_SETREDRAW, 0, 0);
}
public void EndUpdate()
{
SendMessage( this.Handle, WM_SETREDRAW, 1, 0);
Parent.Invalidate(true);
}
Don't know if you've already run into this, but one thing I realized today is that SuspendLayout only suspends layout for the control it's called on...and _not_ for any of its child controls. So if you want to SuspendLayout on the control and all its child controls, you have to write a recursive routine to do so.
Fixing this doesn't solve all the redraw issues, but it can definitely speed things up.
Fixing this doesn't solve all the redraw issues, but it can definitely speed things up.
I use
this.SetStyle(ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer, true);
this.UpdateStyles();
after each constructor and then in the load event I say
this.Visible = true;
this.Update();
Doesn't really solve it entirely but relieves the pain. On second opening its relatively ok - not so much visible drawing. The first call is what takes time and from what I gather you can only work around this perhaps by ngening.
Also check performace on a release build. Things might look a little better also if infragistics controls are running out of GAC it might slow you down a touch.
These 3rd party custom controls carry a penalty one way or another. I swapped out a combobox I was too lazy to owner draw and the performance change was remarkable.
All the best
this.SetStyle(ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer, true);
this.UpdateStyles();
after each constructor and then in the load event I say
this.Visible = true;
this.Update();
Doesn't really solve it entirely but relieves the pain. On second opening its relatively ok - not so much visible drawing. The first call is what takes time and from what I gather you can only work around this perhaps by ngening.
Also check performace on a release build. Things might look a little better also if infragistics controls are running out of GAC it might slow you down a touch.
These 3rd party custom controls carry a penalty one way or another. I swapped out a combobox I was too lazy to owner draw and the performance change was remarkable.
All the best
by10
Friday, February 23, 2007
Friday, February 23, 2007
This topic is archived. No further replies will be accepted.