Optimized Drawing


If you're drawing using page units, transformations, and regions , it's likely that you're heavy into drawing. If that's the case, you'll be interested in ways to optimize your drawings for responsiveness and smooth operation. First and foremost, you'll want to avoid drawing anything that doesn't need drawing. You can do that in one of two ways: redraw only what needs to be redrawn, or don't draw unnecessarily in the first place.

First, invalidate only the portion of your drawing surface that needs to be refreshed. In other words, when drawing the internal state of your form or control, don't invalidate the whole thing if only a small part of the state has changed:

 
 Dim lotsOfNumbers() As Single Function GetRegionWhereNumberIsShow(number As Integer) As Region Property OneNumber() As Single   Set       lotsOfNumbers(0) = Value       ' Don't do this       Me.Invalidate()       ' Do this       Me.Invalidate(GetRegionWhereNumberIsShown(0))   End Set End Property 

The Invalidate function takes an optional rectangle or region as the first argument, so you'll want to invalidate only the portion that needs redrawing, not the entire client area. Now, when the Paint event is triggered, all drawing outside the invalid rectangle will be ignored:

 
 Sub NumbersForm_Paint(sender As Object, e As PaintEventArgs)   Dim i As Integer   For i = 0 to lotsOfNumbers.Length  1       DrawNumber(g, i) ' Will draw only in invalid rectangle   Next End Sub 

Also, there's an optional second argument that says whether to invalidate children. If the state of your children doesn't need updating, don't invalidate.

What's even better than having drawing operations ignored for efficiency? Not drawing at all. Sometimes the client area will be too small to show all of the state. [4] When that happens, there's no need to draw something that lies entirely outside the visible clip region. To determine whether that's the case, you can use the IsVisible method of the Graphics object, which checks to see whether a point or any part of a rectangle is visible in the current clipped region:

[4] This often involves scrolling, which is covered in Chapter 8: Controls.

 
 Function GetNumberRectangle(i As Integer) As Rectangle Sub DrawNumber(g As Graphics, i As Integer)   ' Avoid something that takes a long time to draw   If Not(g.IsVisible(GetNumberRectangle(i)) Then Return   ' Draw something that takes a long time... End Sub 

Be careful when doing the calculations that produce the region to invalidate or checking to see whether a hunk of data is in the invalid region; it may take more cycles to do the checking than it does to simply do the drawing. As always when performance is what you're after, your best bet is to profile various real-world scenarios.

Double Buffering

Another way to make your graphics- intensive programs come out sweet and nice is to eliminate flicker. Flicker is caused by Windows showing things as they're drawn ”whether you're drawing shapes back to front or Windows is erasing the invalid region before you even get a chance to handle the Paint event. [5] To eliminate the problem of displaying the drawing while it's happening, you can use a technique known as double buffering.

[5] You can handle background painting manually by overriding the OnPaintBackground method.

Double buffering is the act of creating a second buffer for the graphics operations to take place in and then, when they're all finished, blowing all the bits onto the screen at once. You can enable double buffering in a form or a control by setting the DoubleBuffer style from the ControlStyles enumeration to true:

 
 Public Sub New()   ' Required for Windows Form Designer support   InitializeComponent()   ' Constructor code after InitializeComponent call   Me.SetStyle(ControlStyles.DoubleBuffer, True) End Sub 

Double buffering by itself solves only half the problem, however, because Windows does your painting in three phases. First, it erases the invalid region by painting it with a Windows-level background brush. Second, it sends the PaintBackground event for your form or control to paint the background, something that your base class generally handles for you using the BackColor and BackgroundImage properties. You can handle it yourself, though:

 
 ' There is no PaintBackground event, only this virtual method Protected Overrides Sub OnPaintBackground(e As PaintEventArgs)   ' Make sure to paint the entire client area or call the   ' base class, or you'll have stuff from below showing through   ' mybase.OnPaintBackground(e)   e.Graphics.FillRectangle(Brushes.Black, Me.ClientRectangle) End Sub 

The third phase of painting is the Paint event handler. Double buffering, by default, collapses the drawing of the PaintBackground and Paint events into a single operation, but the initial erase phase will still show up as flicker. To eliminate the erase phase, you must also set the AllPaintingInWmPaint control style:

 
 Public Sub New()   ' Required for Windows Form Designer support   InitializeComponent()   ' Constructor code after InitializeComponent call   Me.SetStyle(ControlStyles.DoubleBuffer, True)   Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)   ' Needed for controls that are double-buffered   Me.SetStyle(ControlStyles.UserPaint, True) End Sub 

Notice the use of the UserPaint style. This is needed for controls that are double-buffered (and doesn't hurt anything for forms that are double-buffered).

Although double buffering (without the initial erasing of the background) can make all the difference in the user experience, double buffering requires enough memory to capture the entire visible region at the current color quality. At 32 bits per pixel, a 200x200 region requires 156K in additional memory per drawing operation for that region. In memory-constrained systems, this extra memory usage could degrade instead of improve the user experience.

Other Drawing Options

There are a few other drawing- related ControlStyles you may be interested in:

 
 ' Drawing-related control styles Enum ControlStyles   AllPaintingInWmPaint ' Collapse drawing phases into Paint event   DoubleBuffer ' Don't show drawing until Paint event returns   UserPaint ' Control that paints itself specially   Opaque ' OnPaintBackground skipped, Paint draws all client area   ResizeRedraw ' Invalidate entire client area on resize   SupportsTransparentBackColor ' Simulated transparent controls   . . . End Enum 

For example, it's common for controls that need double buffering to want to automatically redraw when they're resized. For this, you use the ResizeRedraw style:

 
 Public Sub New()   ' Required for Windows Form Designer support   InitializeComponent()   ' Double-buffering   Me.SetStyle(ControlStyles.DoubleBuffer, True)   Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)   Me.SetStyle(ControlStyles.UserPaint, True)   ' Redraw when resized   Me.SetStyle(ControlStyles.ResizeRedraw, True) End Sub7 

The ControlStyles settings apply at the point where WinForms starts wrapping the functionality of Windows itself, which is the Control base class (Forms ultimately derive from Control). Several of the ControlStyles settings have nothing to do with drawing but rather govern how the Control class interacts with the underlying operating system. For more information, see the reference documentation for the ControlStyles enumeration.



Windows Forms Programming in Visual Basic .NET
Windows Forms Programming in Visual Basic .NET
ISBN: 0321125193
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net