1. 程式人生 > >淺談C#中的雙緩衝

淺談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();


至此,雙緩衝問題解決,兩種方式的實現效果都一樣,筆者私以為第二種方法佔有的記憶體很少,不會出現記憶體洩露!

以上為網上整理的資料加上筆者自己的陋見,如若有謬誤之處還望指正。