0% found this document useful (0 votes)
17 views7 pages

Double Buffer

Description of techniques in .NET to use a double buffering for windows forms and controls

Uploaded by

rcano
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views7 pages

Double Buffer

Description of techniques in .NET to use a double buffering for windows forms and controls

Uploaded by

rcano
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Double buffering Windows Forms Page 1 of 7

Double Buffering Windows Forms


Now contains code in Visual Basic.

As much as we would like it not to be the case, graphics can be slow enough to
watch as the screen is refreshed. Our eyes, sensitive to movement and particularly
to edge detection so that we don't walk off of cliffs or run into trees, often pick up
the redraw cycle of computer graphics which, at best is mildly annoying or at worst
can cause headaches, eyestrain and in susceptible people, even fits.

Much of this effect is caused by the sequential re-drawing of many graphical


elements such as a chart with many lines or a game with many moving items. In a
perfect world, the sequential redraw would be completely hidden from the user and
the completed graphic presented in its entirety. This is essentially the technique
used in double buffering.

Windows Forms provides an automatic method of double buffering that can be used
by simply setting a few styles in your form or control. For most applications, this is
enough but in certain cases, more control over the process is desirable so manually
double buffering a control is also possible.

First, take a look at the standard and built in method of double buffering. This is
accomplished by setting the styles:

z ControlStyles.AllPaintingInWmPaint
z ControlStyles.UserPaint
z ControlStyles.DoubleBuffer

When these are all set true, the draw process is modified so that instead of your
Paint handler being passed a Graphics for the screen, it is passed a Graphics for an
in-memory bitmap. When you draw to this Graphics object, you are drawing on an
invisible image. At the end of the draw cycle, this bitmap is copied to the main
window automatically and the actual pixels you see are all changed in a fraction of
a second instead of one at a time as the draw cycle progresses.

To set up automatic double buffering for a Form, you would use the following line of
code in the constructor, after the InitializeComponent method call.

C#

this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer,true);

VB

me.SetStyle(
ControlStyles.AllPaintingInWmPaint OR _
ControlStyles.UserPaint OR _
ControlStyles.DoubleBuffer,true)

Manual double buffering can be useful if you don't want the system so make
assumptions for you such as whether the background is opaque or transparent or
perhaps if you want to create a more complex buffering system. There are a few
simple rules that you need to follow to get manual double buffering right.

First, don’t create a new back-buffer every draw cycle. Only create or destroy the

http://www.bobpowell.net/doublebuffer.htm 25/03/2009
Double buffering Windows Forms Page 2 of 7

bitmap when the window's client size changes. Second, only create a bitmap of the
size you need. Clearing pixels takes time and so if there are more pixels than you
need, you're just wasting processor cycles. Lastly, use the simplest draw method to
copy the bitmap to the screen. DrawImageUnscaled is the way to go here.

Before the big demo, listing 1 shows all the salient points of a manual double
buffered application.
private Bitmap _backBuffer;

protected override void OnPaint(PaintEventArgs e)


{
if(_backBuffer==null)
{
_backBuffer=new Bitmap(this.ClientSize.Width,this.ClientSize.Height);
}
Graphics g=Graphics.FromImage(_backBuffer);

//Paint your graphics on g here

g.Dispose();

//Copy the back buffer to the screen

e.Graphics.DrawImageUnscaled(_backBuffer,0,0);

//base.OnPaint (e); //optional but not recommended


}

protected override void OnPaintBackground(PaintEventArgs pevent)


{
//Don't allow the background to paint
}

protected override void OnSizeChanged(EventArgs e)


{
if(_backBuffer!=null)
{
_backBuffer.Dispose();
_backBuffer=null;
}
base.OnSizeChanged (e);
}

VB

Private _backBuffer As Bitmap

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)


If _backBuffer Is Nothing Then
_backBuffer = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
End If

Dim g As Graphics = Graphics.FromImage(_backBuffer)

'Paint on the Graphics object here

g.Dispose()

'Copy the back buffer to the screen


e.Graphics.DrawImageUnscaled(_backBuffer, 0, 0)
End Sub 'OnPaint

'base.OnPaint (e); //optional but not recommended

Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)


End Sub 'OnPaintBackground

'Don't allow the background to paint

Protected Overrides Sub OnSizeChanged(ByVal e As EventArgs)


If Not (_backBuffer Is Nothing) Then
_backBuffer.Dispose()
_backBuffer = Nothing
End If
MyBase.OnSizeChanged(e)
End Sub 'OnSizeChanged

http://www.bobpowell.net/doublebuffer.htm 25/03/2009
Double buffering Windows Forms Page 3 of 7

The following listing shows a practical double buffered application. A checkbox


enables you to choose whether double buffering is used or if the graphics are drawn
directly to the screen for the sake of comparison.

C#
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace DoubleBuffer
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{

private System.Windows.Forms.Timer timer1;


private System.ComponentModel.IContainer components;
private System.Windows.Forms.CheckBox checkBox1;

float _angle;
bool _doBuffer;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code


/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.checkBox1 = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// checkBox1
//
this.checkBox1.Location = new System.Drawing.Point(8, 8);
this.checkBox1.Name = "checkBox1";
this.checkBox1.TabIndex = 0;
this.checkBox1.Text = "Double Buffer";
this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.checkBox1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

http://www.bobpowell.net/doublebuffer.htm 25/03/2009
Double buffering Windows Forms Page 4 of 7

[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void timer1_Tick(object sender, System.EventArgs e)


{
_angle+=3;
if(_angle>359)
_angle=0;
Invalidate();
}

private Bitmap _backBuffer;

protected override void OnPaint(PaintEventArgs e)


{
if(_backBuffer==null)
{
_backBuffer=new Bitmap(this.ClientSize.Width,this.ClientSize.Height);
}

Graphics g=null;
if(_doBuffer)
g=Graphics.FromImage(_backBuffer);
else
g=e.Graphics;

g.Clear(Color.White);

g.SmoothingMode=SmoothingMode.AntiAlias;

Matrix mx=new Matrix();


mx.Rotate(_angle,MatrixOrder.Append);
mx.Translate(this.ClientSize.Width/2,this.ClientSize.Height/2,MatrixOrder.Append);
g.Transform=mx;
g.FillRectangle(Brushes.Red,-100,-100,200,200);

mx=new Matrix();
mx.Rotate(-_angle,MatrixOrder.Append);
mx.Translate(this.ClientSize.Width/2,this.ClientSize.Height/2,MatrixOrder.Append);
g.Transform=mx;
g.FillRectangle(Brushes.Green,-75,-75,149,149);

mx=new Matrix();
mx.Rotate(_angle*2,MatrixOrder.Append);
mx.Translate(this.ClientSize.Width/2,this.ClientSize.Height/2,MatrixOrder.Append);
g.Transform=mx;
g.FillRectangle(Brushes.Blue,-50,-50,100,100);

if(_doBuffer)
{
g.Dispose();

//Copy the back buffer to the screen

e.Graphics.DrawImageUnscaled(_backBuffer,0,0);
}

//base.OnPaint (e); //optional but not recommended


}

protected override void OnPaintBackground(PaintEventArgs pevent)


{
//Don't allow the background to paint
}

protected override void OnSizeChanged(EventArgs e)


{
if(_backBuffer!=null)
{
_backBuffer.Dispose();
_backBuffer=null;
}
base.OnSizeChanged (e);
}

private void checkBox1_CheckedChanged(object sender, System.EventArgs e)


{
_doBuffer=this.checkBox1.Checked;
}
}
}

VB

Imports System
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms

http://www.bobpowell.net/doublebuffer.htm 25/03/2009
Double buffering Windows Forms Page 5 of 7

Imports System.Data

Namespace DoubleBuffer
'/ <summary>
'/ Summary description for Form1.
'/ </summary>

Public Class Form1


Inherits System.Windows.Forms.Form

Private WithEvents timer1 As System.Windows.Forms.Timer


Private components As System.ComponentModel.IContainer
Private WithEvents checkBox1 As System.Windows.Forms.CheckBox

Private _angle As Single


Private _doBuffer As Boolean

Public Sub New()


'
' Required for Windows Form Designer support
'
InitializeComponent()
End Sub 'New

'
' TODO: Add any constructor code after InitializeComponent call
'

'/ <summary>
'/ Clean up any resources being used.
'/ </summary>
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub 'Dispose

#Region "Windows Form Designer generated code"

'/ <summary>
'/ Required method for Designer support - do not modify
'/ the contents of this method with the code editor.
'/ </summary>
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container
Me.timer1 = New System.Windows.Forms.Timer(Me.components)
Me.checkBox1 = New System.Windows.Forms.CheckBox
Me.SuspendLayout()
'
' timer1
'
Me.timer1.Enabled = True
'
' checkBox1
'
Me.checkBox1.Location = New System.Drawing.Point(8, 8)
Me.checkBox1.Name = "checkBox1"
Me.checkBox1.TabIndex = 0
Me.checkBox1.Text = "Double Buffer"
'
' Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 273)
Me.Controls.Add(checkBox1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub 'InitializeComponent
#End Region

'/ <summary>
'/ The main entry point for the application.

http://www.bobpowell.net/doublebuffer.htm 25/03/2009
Double buffering Windows Forms Page 6 of 7

'/ </summary>
<STAThread()> _
Shared Sub Main()
Application.Run(New Form1)
End Sub 'Main

Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs)


Handles timer1.Tick
_angle += 3
If _angle > 359 Then
_angle = 0
End If
Invalidate()
End Sub 'timer1_Tick

Private _backBuffer As Bitmap

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)


If _backBuffer Is Nothing Then
_backBuffer = New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
End If

Dim g As Graphics = Nothing


If _doBuffer Then
g = Graphics.FromImage(_backBuffer)
Else
g = e.Graphics
End If
g.Clear(Color.White)

g.SmoothingMode = SmoothingMode.AntiAlias

Dim mx As New Matrix


mx.Rotate(_angle, MatrixOrder.Append)
mx.Translate(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2,
MatrixOrder.Append)
g.Transform = mx
g.FillRectangle(Brushes.Red, -100, -100, 200, 200)

mx = New Matrix
mx.Rotate(-_angle, MatrixOrder.Append)
mx.Translate(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2,
MatrixOrder.Append)
g.Transform = mx
g.FillRectangle(Brushes.Green, -75, -75, 149, 149)

mx = New Matrix
mx.Rotate(_angle * 2, MatrixOrder.Append)
mx.Translate(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2,
MatrixOrder.Append)
g.Transform = mx
g.FillRectangle(Brushes.Blue, -50, -50, 100, 100)

If _doBuffer Then
g.Dispose()

'Copy the back buffer to the screen


e.Graphics.DrawImageUnscaled(_backBuffer, 0, 0)
End If
End Sub 'OnPaint

'base.OnPaint (e); //optional but not recommended

Protected Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)


End Sub 'OnPaintBackground

'Don't allow the background to paint

Protected Overrides Sub OnSizeChanged(ByVal e As EventArgs)


If Not (_backBuffer Is Nothing) Then
_backBuffer.Dispose()
_backBuffer = Nothing
End If
MyBase.OnSizeChanged(e)
End Sub 'OnSizeChanged

http://www.bobpowell.net/doublebuffer.htm 25/03/2009
Double buffering Windows Forms Page 7 of 7

Private Sub checkBox1_CheckedChanged(ByVal sender As Object, ByVal e As


System.EventArgs) Handles checkBox1.CheckedChanged
_doBuffer = Me.checkBox1.Checked
End Sub 'checkBox1_CheckedChanged
End Class 'Form1
End Namespace 'DoubleBuffer

When running the program looks like this…

Figure 1: Double buffering in action.

Return to the Tips and Tricks index.

Copyright Robert W. Powell 2003-2004. All rights reserved.

Double Buffering How-To


How do you rate this article?

Excellent!
Good
Average
Poor
Awful

Vote!
view results

powered by blogpoll

http://www.bobpowell.net/doublebuffer.htm 25/03/2009

You might also like