Как удвоить буфер управления.NET на форме?

Как я могу установить защищенный DoubleBuffered свойство элементов управления в форме, которые страдают от мерцания?

14 ответов

Решение

Вот более общая версия решения Dummy.

Мы можем использовать отражение, чтобы получить защищенное свойство DoubleBuffered, и тогда оно может быть установлено в true.

Примечание. Вы должны платить налоги разработчика и не использовать двойную буферизацию, если пользователь работает в сеансе служб терминалов (например, удаленный рабочий стол). Этот вспомогательный метод не включает двойную буферизацию, если человек работает на удаленном рабочем столе.

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); 
}

Проверьте эту тему

Повторяя ядро ​​этого ответа, вы можете включить флаг стиля WS_EX_COMPOSITED в окне, чтобы получить как форму, так и все ее элементы управления с двойной буферизацией. Флаг стиля доступен с XP. Это не делает рисование быстрее, но все окно рисуется в закадровом буфере и перетаскивается на экран одним ударом. Заставить его выглядеть мгновенно для глаз пользователя без видимых артефактов рисования. Это не совсем без проблем, некоторые средства визуализации стилей могут давать сбой, особенно TabControl, когда на нем слишком много вкладок. YMMV.

Вставьте этот код в свой класс формы:

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

Большая разница между этой техникой и поддержкой двойной буферизации в Winform заключается в том, что версия Winform одновременно работает только на одном элементе управления. Вы все равно увидите каждую отдельную контрольную краску. Это также может выглядеть как эффект мерцания, особенно если неокрашенный контрольный прямоугольник плохо контрастирует с фоном окна.

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

У Иана есть еще немного информации об использовании этого на терминальном сервере.

public void EnableDoubleBuffering()
{
   this.SetStyle(ControlStyles.DoubleBuffer | 
      ControlStyles.UserPaint | 
      ControlStyles.AllPaintingInWmPaint,
      true);
   this.UpdateStyles();
}

Один из способов - расширить определенный элемент управления, который вы хотите удвоить, и установить свойство DoubleBuffered внутри ctor элемента управления.

Например:

class Foo : Panel
{
    public Foo() { DoubleBuffered = true; }
}

vb.net версия этого прекрасного решения....:

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

nobugz получает кредит за метод в своей ссылке, я просто перепостил. Добавьте это переопределение в форму:

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

Это работало лучше всего для меня, в Windows 7 у меня появлялись большие черные блоки, когда я изменял размеры тяжелой формы элемента управления. Контроль теперь отказов вместо этого! Но это лучше.

Метод расширения для включения или выключения двойной буферизации для элементов управления

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);
    }
}

Использование (например, как сделать DataGridView DoubleBuffered):

DataGridView _grid = new DataGridView();
//  ...
_grid.MakeDoubleBuffered(true);

Перед тем как попробовать двойную буферизацию, посмотрите, решают ли вашу проблему SuspendLayout()/ResumeLayout().

Это вызвало у меня большое горе в течение двух дней с контролем третьей стороны, пока я не выследил его.

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

Недавно у меня было много дыр (помет) при изменении размера / перерисовке элемента управления, содержащего несколько других элементов управления.

Я пробовал WS_EX_COMPOSITED и WM_SETREDRAW, но ничего не получалось, пока я не использовал это:

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

Просто хотел передать это.

Вы также можете наследовать элементы управления в свои собственные классы и установить свойство там. Этот метод также хорош, если вы выполняете много настроек, которые одинаковы для всех элементов управления.

Я создал статический метод, который принимаетControlи устанавливаетDoubleBufferedсобственностьtrueкаждому ребенку рекурсивно.

      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);
}

Я создал собственный календарь, используяDataGridViewкоторый обновляется каждый раз, когда пользователь с ним взаимодействует, этот метод удаляет мерцание, вызывающее мигрень.

Я обнаружил, что простая установка параметра DoubleBuffered в форме автоматически устанавливает все свойства, перечисленные здесь.

FWIW

опираясь на работы тех, кто был до меня:
Dummy's Solution, Ian Boyd's Solution, Amo's Solution

вот версия, которая устанавливает двойную буферизацию через SetStyle в PowerShell с использованием отражения

      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://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.setstyle?view=net-5.0
.LINK
https://docs.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)}}
}
Другие вопросы по тегам