C#中的双缓冲(转)

在编程当中,或多或少会接触到图像编程,对于图像编程来说窗口闪烁是个常见的问题,当窗口有大量的复杂的图元数据需要重绘,或者拥有自定义控件中的窗口闪烁问题更是显而易见的。出现闪烁的原因有很多种,大部分原因主要是因为触发WM_PAINT消息时窗体进行了重绘操作,此过程先是用窗体的背景色擦除窗口表面,再把窗体的图像绘制上去,但是如果这两个操作不在同一时间段完成的话,就会先看到背景色(大部分为白色)接着才看到图像,这样就会出现我们所说的窗体闪烁现象。那么如何解决这个问题呢,解决方法有很多,其中有个比较好的方法(个人认为)就是采用双缓冲机制来绘图,基本上可以解决大部分的问题。

双缓冲的原理:尽量快的输出图像,使输出在一个刷新周期内完成,如果输出内容很多比较慢,那么采用内存缓冲的方法,先把要输出的内容在内存准备好,然后一次性输出到窗体上,简单的说来就是在窗口刷新一次的过程中,让所有图元同时显示到窗口中。

在C#中,.Net Framework为编程人员提供了很好的操作双缓冲的方法,为采用双缓冲机制绘制比较复杂的图像数据带来便捷。

下面简单的介绍在C#中实现双缓冲的几种方法。

一:利用默认的双缓冲

(1)在应用程序中使用双缓冲的最简便的方法是使用 .NET Framework 为窗体和控件提供的默认双缓冲。通过将 DoubleBuffered 属性设置为 true。

this.DoubleBuffered=true;

(2)使用 SetStyle 方法可以为 Windows 窗体和所创作的 Windows 控件启用默认双缓冲,在窗体或者控件的构造函数中添加如下代码即可:

SetStyle(ControlStyles.ResizeRedraw,true);  
SetStyle(ControlStyles.OptimizedDoubleBuffer,true);  
SetStyle(ControlStyles.AllPaintingInWmPaint,true);this.SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);  
this.UpdateStyles(); 

注:

.net1.1 和 .net 2.0 在处理控件双缓冲上是有区别的。

.net 1.1 中,使用:this.SetStyle(ControlStyles.DoubleBuffer, true); 
.net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

二:手动管理双缓冲

在 C# 中手动管理缓冲图像有两种方法,一种是利用单独开辟内存实现双缓冲这种传统的方法,还有一种是利用 .Net Framework 中独有的BufferedGraphicsContext类实现。

方法一: 自己开辟一个缓冲区(如一个不显示的Bitmap对象),在其中绘制完成后,再一次性显示,代码如下:

//1、在内存中建立一块“虚拟画布”  
Bitmap bmp = new Bitmap(200,200);  

//2、获取这块内存画布的Graphics引用
Graphics bufferGraphics = Graphics.FromImage(bmp); 

//3、在这块内存画布上绘图
bufferGraphics.Clear(this.BackColor);  
bufferGraphics.DrawRectangle(Pens.Black,0,0,bmp.Width -1,bmp.Height -1);  
bufferGraphics.DrawEllipse(Pens.Red,10,10,100,50);  
bufferGraphics.DrawLine(Pens.Green,10,100,100,200);  

//4、将内存画布画到窗口中
using(Graphics g = e.Graphics)
{
	g.DrawImage(bmp, 10, 10);
}

//5. 释放资源  
bmp.Dispose();
bufferGraphics.Dispose();

方法二:

对于更高级的双缓存情形,可以使用 .NET Framework 类实现自己的双缓存逻辑。负责单独分配和管理图形缓冲区的类是BufferedGraphicsContext 类。每个应用程序都有自己的默认 BufferedGraphicsContext 来管理此应用程序的所有默认双缓冲。提供调用Current 可以检索对此实例的引用。通过调用 Allocate 方法可以创建与屏幕上的绘图图面关联的 BufferedGraphics 类的实例。此方法创建一个与特定呈现图面(如窗体或控件)关联的 BufferedGraphics 实例。创建 BufferedGraphics 实例后,可以将图形绘制到由该实例的Graphics 属性表示的缓冲区。执行所有图形操作后,可通过调用 Render 方法将缓冲区的内容复制到屏幕上。以下代码把方法一实现的效果用此方法来实现:

BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;  
  
BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics,e.ClipRectangle);  
  
Graphics g = myBuffer.Graphics;  
  
g.Clear(this.BackColor);  
g.DrawRectangle(Pens.Black, 10, 10, 200, 200);  
g.DrawEllipse(Pens.Red, 10, 10, 100, 50);  
g.DrawLine(Pens.Green, 10, 100, 100, 200);  
  
myBuffer.Render(e.Graphics);  //呈现图像至关联的Graphics  
  
myBuffer.Dispose();  
g.Dispose();  

至此,双缓冲问题解决,两种方式的实现效果都一样,私以为第二种方法占有的内存很少,不会出现内存泄露!

成员名称说明
ContainerControl如果为 true,则控件是类似容器的控件。
UserPaint如果为 true,控件将自行绘制,而不是通过操作系统来绘制。 如果为 false,将不会引发 Paint 事件。 此样式仅适用于派生自 Control 的类。
Opaque如果为 true,则控件被绘制为不透明的,不绘制背景。
ResizeRedraw如果为 true,则在调整控件大小时重绘控件。
FixedWidth如果为 true,则自动缩放时,控件具有固定宽度。 例如,如果布局操作尝试重新缩放控件以适应新的Font,则控件的 Width 将保持不变。
FixedHeight如果为 true,则自动缩放时,控件具有固定高度。 例如,如果布局操作尝试重新缩放控件以适应新的Font,则控件的 Height 将保持不变。
StandardClick如果为 true,则控件将实现标准 Click 行为。
Selectable如果为 true,则控件可以接收焦点。
UserMouse如果为 true,则控件完成自己的鼠标处理,因而鼠标事件不由操作系统处理。
SupportsTransparentBackColor如果为 true,控件接受 alpha 组件小于 255 的 BackColor 以模拟透明。 仅在 UserPaint 位设置为 true并且父控件派生自 Control 时才模拟透明。
StandardDoubleClick如果为 true,则控件将实现标准 DoubleClick 行为。 如果 StandardClick 位未设置为 true,则忽略此样式。
AllPaintingInWmPaint如果为 true,控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁。 仅当 UserPaint 位设置为 true 时,才应当应用该样式。
CacheText如果为 true,控件保留文本的副本,而不是在每次需要时从 Handle 获取文本副本。 此样式默认为false。 此行为提高了性能,但使保持文本同步变得困难。
EnableNotifyMessage如果为 true,则为发送到控件的 WndProc 的每条消息调用 OnNotifyMessage 方法。 此样式默认为false。 EnableNotifyMessage 在部分可信的情况下不工作。
DoubleBuffer如果为 true,则绘制在缓冲区中进行,完成后将结果输出到屏幕上。 双重缓冲区可防止由控件重绘引起的闪烁。 如果将 DoubleBuffer 设置为 true,则还应当将 UserPaint 和 AllPaintingInWmPaint 设置为true。
OptimizedDoubleBuffer如果为 true,则该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁。 如果将此属性设置为 true,则还应当将 AllPaintingInWmPaint 设置为 true。
UseTextForAccessibility指定该控件的 Text 属性的值,如果已设置,则可确定该控件的默认 Active Accessibility 名称和快捷键。
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值