Much has been written 
here and 
here about double buffering.
Here you can read how DB is implemented in 
Java .
I will tell how double buffering is implemented on C #. Much of what is written here can be read in MSDN, but without implementation details.
')
Manual double buffering (hereinafter referred to as DB)
For manual double buffering, the .NET Framework provides the following 3 classes:
BufferedGraphicsManager
The 
BufferedGraphicsManager class is used to access (through the static 
Current property) an object of the 
BufferedGraphicsContext class associated with the current application domain ( 
AppDomain ). In fact, 
Current returns a 
BufferedGraphicsContext class object created in the static constructor. Here is the source code of the 
BufferedGraphicsManager class:
public sealed class BufferedGraphicsManager { private static BufferedGraphicsContext bufferedGraphicsContext; public static BufferedGraphicsContext Current { get { return BufferedGraphicsManager.bufferedGraphicsContext; } } static BufferedGraphicsManager() { BufferedGraphicsManager.bufferedGraphicsContext = new BufferedGraphicsContext(); AppDomain.CurrentDomain.ProcessExit += new EventHandler(BufferedGraphicsManager.OnShutdown); AppDomain.CurrentDomain.DomainUnload += new EventHandler(BufferedGraphicsManager.OnShutdown); } private static void OnShutdown(object sender, EventArgs e) { BufferedGraphicsManager.Current.Invalidate(); } } 
From this code, you can see that the object of the class 
BufferedGraphicsContext stored inside the 
BufferedGraphicsManager is destroyed when the current application domain is unloaded.
BufferedGraphicsContext
BufferedGraphicsContext provides the creation (and also destruction) of a new 
BufferedGraphics instance based on the 
Graphics object, providing for this a single 
Allocate method:
 public BufferedGraphics Allocate(Graphics targetGraphics, Rectangle targetRectangle) { if (targetRectangle.Width * targetRectangle.Height > this.MaximumBuffer.Width * this.MaximumBuffer.Height) return this.AllocBufferInTempManager(targetGraphics, IntPtr.Zero, targetRectangle); else return this.AllocBuffer(targetGraphics, IntPtr.Zero, targetRectangle); } 
The method takes as a parameter a 
Graphics object and an area on it for which you want to create a buffer.
If the area of ​​this area does not exceed the area specified by the 
MaximumBuffer property, the 
AllocBuffer method is 
called and the resulting 
BufferedGraphics object is returned. The 
AllocBuffer method creates inside of itself (using the 
CreateBuffer method described below), a new offscreen graph, wraps it in 
BufferedGraphics , saves it into a variable of the 
buffer object and returns it. This variable is used to further, when destroying an instance of 
BufferedGraphicsContext (using the 
Dispose method), to destroy the associated instance of 
BufferedGraphics .
The creationBuffer method is responsible for creating an offscreen (i.e., stored only in memory without displaying on the screen) of the 
Graphics instance . Using the native function 
CreateDIBSection, it creates a “hardware-independent bitmap” (DIB), on the basis of which it creates a new 
Graphics object, and returns it as a result.
If the area of ​​the transferred area exceeds the 
MaximumBuffer area, then the 
AllocBufferInTempManager method is 
called , the source code of which is given below:
 private BufferedGraphics AllocBufferInTempManager(Graphics targetGraphics, IntPtr targetDC, Rectangle targetRectangle) { //   ""  var bufferedGraphicsContext= new BufferedGraphicsContext(); //      (graphics),    ( context)  //     var bufferedGraphics = bufferedGraphicsContext.AllocBuffer(targetGraphics, targetDC, targetRectangle); // ,   ,   bufferedGraphics //        // bufferedGraphicsContext: bufferedGraphics.DisposeContext = true; return bufferedGraphics; } 
From this code, you can see that inside the 
AllocBufferInTempManager method, a new instance of 
BufferedGraphicsContext is created , in which the 
AllocBuffer method is 
called , and the resulting 
BufferedGraphics is returned as a result. Moreover, the temporary 
BufferedGraphicsContext object created is not immediately destroyed, but only when the 
BufferedGraphics created by it is destroyed. For this, 
BufferedGraphics keeps a reciprocal link to its creator, and when destroyed, if the 
DisposeContext property is 
true , it takes it with itself.
BufferedGraphics
The 
BufferedGraphics class is very small. Its source code takes a little more than 100 lines. It is a simple wrapper over a 
Graphics object, and provides a Render method for copying it onto another 
Graphics :
 public void Render(Graphics target) 
Copying is done by the native BitBlt function.
Automatic db
The simplest way to use db to render a control is to turn on automatic db for the desired control:
 control.DoubleBuffered = true; 
or
 control.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 
Consider what happens to the control when we enable automatic DB for it. The 
DoubleBuffered property, as well as the 
SetStyle method, is located in the 
Control class. Let's look at the source code of this class. The code for the 
DoubleBuffered property looks like this:
  protected virtual bool DoubleBuffered { get { return this.GetStyle(ControlStyles.OptimizedDoubleBuffer); } set { if (value != this.DoubleBuffered) { if (value) this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, value); else this.SetStyle(ControlStyles.OptimizedDoubleBuffer, value); } } } 
As you can see from this code snippet, the above 2 ways of including dBs do not differ from each other, except that the 
ControlStyles.AllPaintingInWmPaint flag is set in the 
DoubleBuffered setter. But since this flag is also set in the constructor of the control 
1 , then if you did not reset it manually, both of these methods have the same effect.
From the source code of the 
Control class, you can also see that the 
ControlStyles.AllPaintingInWmPaint flag is checked only inside the private 
WmEraseBkgnd method (and is set only in the constructor and setter of the 
DoubleBuffered property), which is responsible for processing the 
WM_ERASEBKGND 2 system message, and when it is received, it draws the background of the control. Here is its implementation:
  private void WmEraseBkgnd(ref Message m) { if (this.GetStyle(ControlStyles.UserPaint) && !this.GetStyle(ControlStyles.AllPaintingInWmPaint)) { ... using (PaintEventArgs e = new PaintEventArgs(wparam, ClientRect)) this.PaintWithErrorHandling(e, (short) 1); } ... } 
This shows that if the 
AllPaintingInWmPaint flag 
is NOT set, then when the window 
receives a WM_ERASEBKGND message, it calls the 
PaintWithErrorHandling method, with the 
layer parameter equal to 1 
3 , which in turn causes the redrawing of the background of the control 
4 .
It is also worth considering the 
ControlStyles.UserPaint flag. This flag indicates that the contents of the control will be rendered by the Framework .NET tools, and not by the system tools. For example, if you specify a background image for your form, and 
reset the UserPaint flag, the picture will not be drawn.
The main actions on DB are deployed inside the 
WmPaint method. This method is responsible for processing the 
WM_PAINT system message, which arrives when a section of the control needs to be redrawn. The 
WmPaint method is private and only called from the 
WndProc method, provided that the 
ControlStyles.UserPaint flag is set:
 protected virtual void WndProc(ref Message m) { switch (m.Msg) { ... case WM_PAINT: if (this.GetStyle(ControlStyles.UserPaint)) this.WmPaint(ref m); break; ... } } 
If we omit the details not related to the DB, the implementation of the 
WmPaint method is as follows:
 private void WmPaint(ref Message m) { if (this.DoubleBuffered || this.GetStyle(ControlStyles.AllPaintingInWmPaint) && this.DoubleBufferingEnabled) { IntPtr num; //   Graphics Rectangle rectangle; //    //  num  rectangle... if (rectangle.Width > 0 && rectangle.Height > 0) { Rectangle clientRectangle = this.ClientRectangle; using (BufferedGraphics bufferedGraphics = BufferedGraphicsManager.Current.Allocate(num, clientRectangle)) { Graphics graphics = bufferedGraphics.Graphics; graphics.SetClip(rectangle); System.Drawing.Drawing2D.GraphicsState gstate = graphics.Save(); using (PaintEventArgs e = new PaintEventArgs(graphics, rectangle)) { this.PaintWithErrorHandling(e, (short) 1, false); graphics.Restore(gstate); this.PaintWithErrorHandling(e, (short) 2, false); bufferedGraphics.Render(); } } } ... } else { //    ... } } 
As can be seen from the above code snippet, in order for graphics to be drawn with 
db, DoubleBuffered should be 
true , or 
ControlStyles flag should be set. 
AllPaintingInWmPaint ( 
DoubleBufferingEnabled is not taken into account because it is always 
true 5 here ).
Next, using the default BufferedGrpahicsContext, a graphic buffer is created.
It sets a draw rectangle equal to the area for which a redraw is required and the current state is saved.
After that, the OnBackgroundPaint and OnPaint methods are called for it through a call to the PaintWithErrorHandling 
3 method, and the resulting image is copied into the graphics control.
As it can be seen, the same DB method is used for automatic buffering as for manual one.
1. A fragment of the source code of the 
Control class constructor in which the 
ControlStyles flags are set:
  internal Control(bool autoInstallSyncContext) { ... this.SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick | ControlStyles.Selectable | ControlStyles.StandardDoubleClick | ControlStyles.AllPaintingInWmPaint | ControlStyles.UseTextForAccessibility, true); ... } 
2. The 
WM_ERASEBKGND message arrives when the control is resized. Source code fragment in which the 
WM_ERASEBKGND message is 
processed :
  protected virtual void WndProc(ref Message m) { switch (m.Msg) { ... case WM_ERASEBKGND: this.WmEraseBkgnd(ref m); break; ... } } 
3. Implementation of the PaintWithErrorHandling method:
 private void PaintWithErrorHandling(PaintEventArgs e, short layer) { ... switch (layer) { case (short) 1: if (!this.GetStyle(ControlStyles.Opaque)) this.OnPaintBackground(e); break; case (short) 2: this.OnPaint(e); break; } ... } 
4. The background of the control is not redrawn if the 
ControlStyles.Opaque flag is set.
5. Private property 
DoubleBufferingEnabled , has the following implementation:
  bool DoubleBufferingEnabled { private get { return this.GetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer); } } 
Since the WmPaint method is called only if the 
ControlStyles.UserPaint flag is set, 
DoubleBufferingEnabled will always be 
true here . And since it is closed and is not checked anywhere except 
WmPaint , its purpose is not clear.