58

How can I set the protected DoubleBuffered property of the controls on a form that are suffering from flicker?

JYelton
  • 35,664
  • 27
  • 132
  • 191
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219

14 Answers14

95

Here's a more generic version of Dummy's solution.

We can use reflection to get at the protected DoubleBuffered property, and then it can be set to true.

Note: You should pay your developer taxes and not use double-buffering if the user is running in a terminal services session (e.g. Remote Desktop) This helper method will not turn on double buffering if the person is running in remote desktop.

public static void SetDoubleBuffered(System.Windows.Forms.Control c)
{
   //Taxes: Remote Desktop Connection and painting
   //http://blogs.msdn.com/oldnewthing/archive/2006/01/03/508694.aspx
   if (System.Windows.Forms.SystemInformation.TerminalServerSession)
      return;

   System.Reflection.PropertyInfo aProp = 
         typeof(System.Windows.Forms.Control).GetProperty(
               "DoubleBuffered", 
               System.Reflection.BindingFlags.NonPublic | 
               System.Reflection.BindingFlags.Instance);

   aProp.SetValue(c, true, null); 
}
Richard Neish
  • 8,414
  • 4
  • 39
  • 69
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • Funny, I'd think that it's even more important to double-buffer when remoting, so that you avoid needlessly sending a bunch of repaints over the wire? – Robert Jeppesen Jun 11 '09 at 14:50
  • 15
    It's exactly what you don't want. In a terminal session the GDI system can send commands (draw line here, draw circle here, fill here, etc). Double buffering is accomplished by you drawing everyting onto a bitmap and then using GDI to paint your entire form as a bitmap. Sending an uncompressed bitmap over the wire is *MUCH* slower than sending the origianl GDI commands. – Ian Boyd Jun 12 '09 at 18:50
  • Doesn't help prevent an auto-sized TextBox from flickering on resize... In fact nothing I have tried so far does. – Roman Starkov Aug 26 '09 at 08:57
  • 3
    @Boris That's because the Windows `TEXTBOX` control [doesn't obey any](http://stackoverflow.com/questions/1955538/win32-how-to-custom-draw-an-edit-control) laws of painting. – Ian Boyd Nov 30 '12 at 18:51
  • 2
    @romkyns, If you need a double-buffered `TextBox`, use a `RichTextBox` with `DetectUrls` set to False. If you want it to be editable, strip the formatting using the [EM_SETCHARFORMAT](http://msdn.microsoft.com/en-us/library/windows/desktop/bb774230.aspx) and [EM_SETPARAFORMAT](http://msdn.microsoft.com/en-us/library/windows/desktop/bb774276.aspx) messages (example code [is here](http://social.msdn.microsoft.com/Forums/windows/en-US/596ee31b-1eff-4ac9-a68f-b46d7c4059ae/how-do-i-set-the-richtextboxselectionfont-fontfamily-without-changing-the-style)). – alldayremix Jun 27 '13 at 22:16
  • @IanBoyd I know this is an extremely old question - but thought I'd give the a try anyway. I had the same issue with my app, so I converted the above code to VB.NET and added it into my main form class. I then added `SetDoubleBuffered(SplashScreen)` immediately before `SplashScreen.Show()` in my `DisplaySplash` `Sub`. It works! However I'm completely new to this kind of thing, and am unsure if this is the correct way to implement this? –  Dec 29 '20 at 23:03
  • Hmm, so what you're saying is that `TerminalServerSession` is an exceptional case that you should probably look out for at run-time? – Gregor y Apr 22 '21 at 15:40
  • @Gregory Correct. Along with respecting the user's font family, font size, color choices, high-contrast setting, disable animation settings, allow keyboard shortcuts for those who can't use a mouse. You know: things that almost no application does anymore. – Ian Boyd Apr 22 '21 at 17:41
  • huh, I haven't used an IDE in ages, but I would have thought that stuff got shoved down into the code-folds of VS somewhere. Also, is there a benefit to setting the property vs using the same reflection technique to call `SetStyle` with the 3 flags `DoubleBuffer | UserPaint | AllPaintingInWmPaint` – Gregor y Apr 22 '21 at 23:44
72

Check this thread

Repeating the core of that answer, you can turn on the WS_EX_COMPOSITED style flag on the window to get both the form and all of its controls double-buffered. The style flag is available since XP. It doesn't make painting faster but the entire window is drawn in an off-screen buffer and blitted to the screen in one whack. Making it look instant to the user's eyes without visible painting artifacts. It is not entirely trouble-free, some visual styles renderers can glitch on it, particularly TabControl when its has too many tabs. YMMV.

Paste this code into your form class:

protected override CreateParams CreateParams {
    get {
        var cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;    // Turn on WS_EX_COMPOSITED
        return cp;
    } 
}

The big difference between this technique and Winform's double-buffering support is that Winform's version only works on one control at at time. You will still see each individual control paint itself. Which can look like a flicker effect as well, particularly if the unpainted control rectangle contrasts badly with the window's background.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
19
System.Reflection.PropertyInfo aProp = typeof(System.Windows.Forms.Control)
    .GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic |
    System.Reflection.BindingFlags.Instance);
aProp.SetValue(ListView1, true, null);

Ian has some more information about using this on a terminal server.

Community
  • 1
  • 1
dummy
  • 4,256
  • 3
  • 25
  • 36
12
public void EnableDoubleBuffering()
{
   this.SetStyle(ControlStyles.DoubleBuffer | 
      ControlStyles.UserPaint | 
      ControlStyles.AllPaintingInWmPaint,
      true);
   this.UpdateStyles();
}
Arno
  • 2,169
  • 1
  • 15
  • 12
9

One way is to extend the specific control you want to double buffer and set the DoubleBuffered property inside the control's ctor.

For instance:

class Foo : Panel
{
    public Foo() { DoubleBuffered = true; }
}
Jeff Hubbard
  • 9,822
  • 3
  • 30
  • 28
5

Extension method to turn double buffering on or off for controls

public static class ControlExtentions
{
    /// <summary>
    /// Turn on or off control double buffering (Dirty hack!)
    /// </summary>
    /// <param name="control">Control to operate</param>
    /// <param name="setting">true to turn on double buffering</param>
    public static void MakeDoubleBuffered(this Control control, bool setting)
    {
        Type controlType = control.GetType();
        PropertyInfo pi = controlType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
        pi.SetValue(control, setting, null);
    }
}

Usage (for example how to make DataGridView DoubleBuffered):

DataGridView _grid = new DataGridView();
//  ...
_grid.MakeDoubleBuffered(true);
MajesticRa
  • 13,770
  • 12
  • 63
  • 77
5

nobugz gets the credit for the method in his link, I'm just reposting. Add this override to the Form:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;
        return cp;
    }
}

This worked best for me, on Windows 7 I was getting large black blocks appearing when I resize a control heavy form. The control now bounce instead! But it's better.

Community
  • 1
  • 1
Chris S
  • 64,770
  • 52
  • 221
  • 239
4

This caused me a lot of grief for two days with a third party control until I tracked it down.

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;
        return cp;
    }
}

I recently had a lot of holes (droppings) when re-sizing / redrawing a control containing several other controls.

I tried WS_EX_COMPOSITED and WM_SETREDRAW but nothing worked until I used this:

private void myPanel_SizeChanged(object sender, EventArgs e)
{
     Application.DoEvents();
}

Just wanted to pass it on.

dnennis
  • 140
  • 1
  • 9
4

vb.net version of this fine solution....:

Protected Overrides ReadOnly Property CreateParams() As CreateParams
    Get
        Dim cp As CreateParams = MyBase.CreateParams
        cp.ExStyle = cp.ExStyle Or &H2000000
        Return cp
    End Get
End Property
Flip70
  • 41
  • 2
4

Before you try double buffering, see if SuspendLayout()/ResumeLayout() solve your problem.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
1

You can also inherit the controls into your own classes, and set the property in there. This method is also nice if you tend to be doing a lot of set up that is the same on all of the controls.

MagicKat
  • 9,695
  • 6
  • 32
  • 43
0

I created a static method that accepts a Control and sets the DoubleBuffered property to true to each and every children recursively.

public static void CascadingDoubleBuffer(Control c)
{
    var p = c.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
    p?.SetValue(c, true, null);
    foreach (Control cc in c.Controls) CascadingDoubleBuffer(cc);
}

I created a custom calendar using DataGridView that refreshes everytime the user interacts with it, this method removes the migraine-inducing flicker.

RG Orobia
  • 36
  • 3
0

I have found that simply setting the DoubleBuffered setting on the form automatically sets all the properties listed here.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
ljs
  • 37,275
  • 36
  • 106
  • 124
-1

FWIW

building on the work of those who've come before me:
Dummy's Solution, Ian Boyd's Solution, Amo's Solution

here is a version that sets double buffering via SetStyle in PowerShell using reflection

function Set-DoubleBuffered{
<#
.SYNOPSIS
Turns on double buffering for a [System.Windows.Forms.Control] object
.DESCRIPTION
Uses the Non-Public method 'SetStyle' on the control to set the three
style flags recomend for double buffering: 
   UserPaint
   AllPaintingInWmPaint
   DoubleBuffer
.INPUTS
[System.Windows.Forms.Control]
.OUTPUTS
None
.COMPONENT  
System.Windows.Forms.Control
.FUNCTIONALITY
Set Flag, DoubleBuffering, Graphics
.ROLE
WinForms Developer
.NOTES
Throws an exception when trying to double buffer a control on a terminal 
server session becuase doing so will cause lots of data to be sent across 
the line
.EXAMPLE
#A simple WinForm that uses double buffering to reduce flicker
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()

$Pen = [System.Drawing.Pen]::new([System.Drawing.Color]::FromArgb(0xff000000),3)

$Form = New-Object System.Windows.Forms.Form
Set-DoubleBuffered $Form
$Form.Add_Paint({
   param(
      [object]$sender,
      [System.Windows.Forms.PaintEventArgs]$e
   )
   [System.Windows.Forms.Form]$f = $sender
   $g = $e.Graphics
   $g.SmoothingMode = 'AntiAlias'
   $g.DrawLine($Pen,0,0,$f.Width/2,$f.Height/2)
})
$Form.ShowDialog()

.LINK
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.setstyle?view=net-5.0
.LINK
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.controlstyles?view=net-5.0
#>
   param(
      [parameter(mandatory=$true,ValueFromPipeline=$true)]
      [ValidateScript({$_ -is [System.Windows.Forms.Control]})]
      #The WinForms control to set to double buffered
      $Control,
      
      [switch]
      #Override double buffering on a terminal server session(not recomended)
      $Force
   )
   begin{try{
      if([System.Windows.Forms.SystemInformation]::TerminalServerSession -and !$Force){
         throw 'Double buffering not set on terminal server session.'
      }
      
      $SetStyle = ([System.Windows.Forms.Control]).GetMethod('SetStyle',
         [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance
      )
      $UpdateStyles = ([System.Windows.Forms.Control]).GetMethod('UpdateStyles',
         [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance
      )
   }catch {$PSCmdlet.ThrowTerminatingError($PSItem)}
   }process{try{
      $SetStyle.Invoke($Control,@(
         ([System.Windows.Forms.ControlStyles]::UserPaint -bor
           [System.Windows.Forms.ControlStyles]::AllPaintingInWmPaint -bor
           [System.Windows.Forms.ControlStyles]::DoubleBuffer
         ),
         $true
      ))
      $UpdateStyles.Invoke($Control,@())
   }catch {$PSCmdlet.ThrowTerminatingError($PSItem)}}
}
Gregor y
  • 1,762
  • 15
  • 22