Double Buffering and Flicker-Free Drawing
Do you remember the Web drawing method in Chapter 12? Drawing on the Web works differently from drawing in Windows Forms. On the Web we have many limitations, one of which is no pixelwise drawing support in the Web browser. So our approach in Chapter 12 was to convert our graphics objects into a temporary bitmap image and view the image in a Web browser.
Double buffering is a similar concept. You may have seen one of the frequently asked questions on GDI+ discussion forums: "How do we create flicker-free drawings"? The double buffering technique is used to provide faster, smoother drawings by reducing flicker. In this technique, all objects are drawn on an off-screen canvas with the help of a temporary image and a Graphics object. The image is then copied to the control. If the drawing operation is small and includes drawing only simple objects such as rectangles or lines, there is no need for double buffering (it may even degrade performance). If there are many calculations or drawn elements, performance and appearance may be greatly improved through the use of double buffering.
To prove the point, let's write an example. Listing 13.8 gives the code for a drawing method that draws several lines.
Listing 13.8 The DrawLines method
private void DrawLines(Graphics g) { float width = ClientRectangle.Width; float height = ClientRectangle.Height; float partX = width / 1000; float partY = height / 1000; for (int i = 0; i < 1000; i++) { g.DrawLine(Pens.Blue, 0, height - (partY * i), partX * i, 0); g.DrawLine(Pens.Green, 0, height - (partY * i), (width) - partX * i, 0); g.DrawLine(Pens.Red, 0, partY * i, (width) - partX * i, 0); } }
To test our application, we will call it from a button click. The code for a button click event handler is given in Listing 13.9.
Listing 13.9 Calling the DrawLines method
// Create a Graphics object for "this" Graphics g = this.CreateGraphics(); g.Clear(this.BackColor); // Draw lines DrawLines(g); // Dispose of object g.Dispose();
Figure 13.4 shows the output from Listing 13.9.
Figure 13.4. Drawing lines in a loop
Now let's draw the same lines using a Bitmap object. We create a temporary Graphics object from a temporary image and call its draw and fill methods. Instead of calling DrawLine with respect to a form, we call DrawImage, which draws the image generated by the DrawLine method.
As Listing 13.10 shows, we create a Bitmap object in a buffer and send the entire buffer all at once using DrawImage. We add the code given in Listing 13.10 on the Bitmap Draw button click event handler.
Listing 13.10 Using double buffering to draw
Graphics g = this.CreateGraphics(); g.Clear(this.BackColor); // Create a Bitmap object with the size of the form Bitmap curBitmap = new Bitmap(ClientRectangle.Width, ClientRectangle.Height); // Create a temporary Graphics object from the bitmap Graphics g1 = Graphics.FromImage(curBitmap); // Draw lines on the temporary Graphics object DrawLines(g1); // Call DrawImage of Graphics and draw bitmap g.DrawImage(curBitmap, 0, 0); // Dispose of objects g1.Dispose(); curBitmap.Dispose(); g.Dispose();
Comparing the two methods given in Listings 13.9 and 13.10 reveals a significant difference in drawing performance. In Listing 13.9, drawing begins as soon as we hit the Simple Draw button and continues until it is done. By contrast, when we hit the Bitmap Draw button, drawing doesn't start immediately. This method actually draws on an in-memory Bitmap object, and when all drawing is done, it displays the bitmap.