淺談C#中的雙緩衝

  在編程當中,或多或少會接觸到圖像編程,對於圖像編程來說窗口閃爍是個常見的問題,當窗口有大量的複雜的圖元數據需要重繪,或者擁有自定義控件中的窗口閃爍問題更是顯而易見的。出現閃爍的原因有很多種,大部分原因主要是因爲觸發WM_PAINT消息時窗體進行了重繪操作,此過程先是用窗體的背景色擦除窗口表面,再把窗體的圖像繪製上去,但是如果這2個操作不在同一時間段完成的話,就會先看到背景色(大部分爲白色)接着纔看到圖像,這樣就會出現我們所說的窗體閃爍現象。那麼如何解決這個問題呢,解決方法有很多,其中有個比較好的方法(個人認爲)就是採用雙緩衝機制來繪圖,基本上可以解決大部分的問題。

      雙緩衝的原理:儘量快的輸出圖像,使輸出在一個刷新週期內完成,如果輸出內容很多比較慢,那麼採用內存緩衝的方法,先把要輸出的內容在內存準備好,然後一次性輸出到窗體上,簡單的說來就是在窗口刷新一次的過程中,讓所有圖元同時顯示到窗口中。

     在C#中 .Net Framework爲編程人員提供了很好的操作雙緩衝的方法,爲採用雙緩衝機制繪製比較複雜的圖像數據帶來便捷。下面簡單的介紹在C#中實現雙緩衝的幾種方法。

 一:利用默認的雙緩衝

(1)在應用程序中使用雙緩衝的最簡便的方法是使用 .NET Framework 爲窗體和控件提供的默認雙緩衝。通過將 DoubleBuffered 屬性設置爲 true。          

  1. this.DoubleBuffered=true;  
     this.DoubleBuffered=true;

(2)使用 SetStyle 方法可以爲 Windows 窗體和所創作的 Windows 控件啓用默認雙緩衝,在窗體或者控件的構造函數中添加如下代碼即可:

  1. SetStyle(ControlStyles.ResizeRedraw,true);  
  2. SetStyle(ControlStyles.OptimizedDoubleBuffer,true);  
  3. SetStyle(ControlStyles.AllPaintingInWmPaint,true);  
     SetStyle(ControlStyles.ResizeRedraw,true);
     SetStyle(ControlStyles.OptimizedDoubleBuffer,true);
     SetStyle(ControlStyles.AllPaintingInWmPaint,true);

          或者:

  1. this.SetStyle(ControlStyles.ResizeRedraw |  
  2.               ControlStyles.OptimizedDoubleBuffer |  
  3.               ControlStyles.AllPaintingInWmPaint, true);  
  4. this.UpdateStyles();  
     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. //1、在內存中建立一塊“虛擬畫布”   
  2.   Bitmap bmp = new Bitmap(200,200);  
  3.   
  4. //2、獲取這塊內存畫布的Graphics引用   
  5.   Graphics bufferGraphics = Graphics.FromImage(bmp);  
  6.   
  7. //3、在這塊內存畫布上繪圖   
  8.   bufferGraphics.Clear(this.BackColor);  
  9. bufferGraphics.DrawRectangle(Pens.Black,0,0,bmp.Width -1,bmp.Height -1);  
  10. bufferGraphics.DrawEllipse(Pens.Red,10,10,100,50);  
  11. bufferGraphics.DrawLine(Pens.Green,10,100,100,200);  
  12.   
  13. //4、將內存畫布畫到窗口中   
  14.   using(Graphics g = e.Graphics)  
  15.   {  
  16.       g.DrawImage(bmp, 10, 10);  
  17.   }  
  18.     
  19. //5. 釋放資源   
  20.   bmp.Dispose();  
  21. bufferGraphics.Dispose();     
          //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 方法將緩衝區的內容複製到屏幕上。 以下代碼把方法一實現的效果用此方法來實現:

  1. BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;  
  2.   
  3. BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics,e.ClipRectangle);  
  4.   
  5. Graphics g = myBuffer.Graphics;  
  6.   
  7. g.Clear(this.BackColor);  
  8. g.DrawRectangle(Pens.Black, 10, 10, 200, 200);  
  9. g.DrawEllipse(Pens.Red, 10, 10, 100, 50);  
  10. g.DrawLine(Pens.Green, 10, 100, 100, 200);  
  11.   
  12. myBuffer.Render(e.Graphics);  //呈現圖像至關聯的Graphics   
  13.   
  14. myBuffer.Dispose();  
  15. g.Dispose();  
            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();


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

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

發佈了67 篇原創文章 · 獲贊 25 · 訪問量 48萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章