淺談C#中的雙緩衝
在程式設計當中,或多或少會接觸到影象程式設計,對於影象程式設計來說視窗閃爍是個常見的問題,當視窗有大量的複雜的圖元資料需要重繪,或者擁有自定義控制元件中的視窗閃爍問題更是顯而易見的。出現閃爍的原因有很多種,大部分原因主要是因為觸發WM_PAINT訊息時窗體進行了重繪操作,此過程先是用窗體的背景色擦除視窗表面,再把窗體的影象繪製上去,但是如果這2個操作不在同一時間段完成的話,就會先看到背景色(大部分為白色)接著才看到影象,這樣就會出現我們所說的窗體閃爍現象。那麼如何解決這個問題呢,解決方法有很多,其中有個比較好的方法(個人認為)就是採用雙緩衝機制來繪圖,基本上可以解決大部分的問題。
雙緩衝的原理:儘量快的輸出影象,使輸出在一個重新整理週期內完成,如果輸出內容很多比較慢,那麼採用記憶體緩衝的方法,先把要輸出的內容在記憶體準備好,然後一次性輸出到窗體上,簡單的說來就是在視窗重新整理一次的過程中,讓所有圖元同時顯示到視窗中。
在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# 中手動管理緩衝影象有2中方法,一種是利用單獨開闢記憶體實現雙緩衝這種傳統的方法,還有一種是利用 .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();
至此,雙緩衝問題解決,兩種方式的實現效果都一樣,筆者私以為第二種方法佔有的記憶體很少,不會出現記憶體洩露!
以上為網上整理的資料加上筆者自己的陋見,如若有謬誤之處還望指正。