如何DoubleBuffered
在出现闪烁的表单上设置控件的受保护属性?
13 回答
这是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);
Ian有更多关于在终端服务器上使用它的信息。
public void EnableDoubleBuffering()
{
this.SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);
this.UpdateStyles();
}
一种方法是扩展您想要双缓冲的特定控件并在控件的 ctor 内设置 DoubleBuffered 属性。
例如:
class Foo : Panel
{
public Foo() { DoubleBuffered = true; }
}
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();
}
只是想传下去。
这个很好的解决方案的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
您还可以将控件继承到您自己的类中,并在其中设置属性。如果您倾向于在所有控件上进行大量相同的设置,则此方法也很好。
我发现只需在表单上设置 DoubleBuffered 设置就会自动设置此处列出的所有属性。
FWIW
以我之前的人的工作为基础:
Dummy 的解决方案,Ian Boyd 的解决方案,Amo 的解决方案
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)}}
}