爲完成PaintBoardDemo(本人設計的一個基於.NET Framework的WinForm的畫圖程序),過程當中遇到的技術難點之一就是就是要顯示任何圖形繪製過程當中的軌跡,也即須要在pictureBox控件的MouseMove事件中添加相應的Graphics對象的DrawXX Methods.
在設計之初,僅僅可以在MouseMove事件中寫出一行代碼:g.DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y)(ptLast爲Point類型的全局變量,用以保存MouseDown事件的座標)。可想而知,鼠標的每一次Move就將觸發MouseMove事件,即執行一次g.DrawLine(畫一條直線)。所以程序運行的結果就是,畫板上顯示了繪製過程當中的全部軌跡,以下圖,顯然,這是與咱們的設計目的相不符的。
通過對Bitmap與Graphics的學習及通過同窗開導,終於解決了不顯示繪製過程當中多餘軌跡的問題。基本實現的思想能夠總結以下:
1.創建一個位圖對象,即Bitmap _bitmapTemp,和一個Graphics對象用來實現一系列繪製操做,即Graphics g(_bitmapTemp,g均爲全局變量)。
2.在主窗體的Shown事件中,對_bitmapTemp和g進行初始化:
_bitmapTemp=new Bitmap(pictureBox.Width,pictureBox.Height);//初始化位圖大小和pictureBox一致
Graphics.FromImage(_bitmapTemp).FillRectangle(Brushes.White,0,0,pictureBox.Width,pictureBox.Height);//而且將位圖_bitmapTemp以白色填充所有區域,以做繪製之用。
g=pictureBox.CreateGraphics();//實例化g對象,代表之後對g的全部繪製操做均在pictureBox之上。
pictureBox.Image=_bitmapTemp;//「引用傳遞」,將pictureBox.Image&_bitmapTemp指向內存中同一塊位圖,實如今pictureBox顯示實時的繪製結果。
3.pictureBox的MouseMove事件中執行繪製操做:
g.DrawImage(_bitmapTemp,0,0);//在第N次繪製時,將第N-1次繪製所得的結果畫到pictureBox中,既實現了繪製結果的同步顯示,也消除了第N次繪製過程當中產生的軌跡。
g.DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y);//繪製直線。
4.pictureBox的MouseUp事件中將繪製結果繪製到_bitmapTemp,保證繪製結果的正確性(所繪的圖形均被保留):
Graphics.FromImage(_bitmapTemp).DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y);
至此,就能夠在pictureBox中繪製各種圖形了(調用Graphics類的DrawXX Methods便可)。可是會發現整個繪製過程當中,圖形的閃動極其厲害。爲何呢?回過頭來再看代碼,發現問題出在g.DrawImage(_bitmapTemp,0,0);每次MouseMove,就在pictureBox中將以前的圖所有從新繪製一次,每每繪製過程當中觸發的MouseMove事件是不可勝數的,頻繁的重繪全圖,不只效率低下,更帶來了直觀的閃動問題。可是也不可能把這行代碼去掉,它但是解決繪製軌跡問題的核心代碼。
百度了關於閃動優化的方法,發現很多都提到了「雙緩衝"技術,參照着網上的方法,發現閃動問題確實獲得瞭解決。看來有必要對這個雙緩衝技術進行一下學習。
雙緩衝解決閃動問題的原理主要是因爲當啓用雙緩衝時,全部繪製操做均呈現到內存緩衝區,而不是在屏幕上繪製。全部繪製完成以後,內存緩衝區直接複製到與其關聯的繪製區域。由於在屏幕上只執行了一次圖形操做,因此消除了由複雜操做形成的圖像閃爍。
【默認雙緩衝】
在應用程序中啓用雙緩衝的最簡便的方法是使用.NET Framework爲窗體和控件提供的默認雙緩衝。經過將DoubleBuffered屬性設置爲True或者使用SetStyle方法能夠爲Windows窗體和所創做的Windows控件啓用默認雙緩衝。
public void EnableDoubleBuffering()
{
// Set the value of the double-buffering style bits to true.
this.SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);
this.UpdateStyles();
}
【管理和顯示緩衝的圖形】 c#
.NET中負責單獨分配和管理圖形緩衝區的類是BufferedGraphicsContext類。每一個應用程序都有本身的默認BufferedGraphicsContext實例來管理此應用程序的全部默認雙緩衝。大多數狀況下,每一個應用程序只有一個應用程序域,因此每個應用程序一般只有一個BufferedGraphicsContext。默認 BufferedGraphicsContext 實例由 BufferedGraphicsManager 類管理。經過調用 BufferedGraphicsManager.Current 屬性能夠檢索對默認 BufferedGraphicsContext 實例的引用。還能夠建立一個專用的 BufferedGraphicsContext 實例以提升圖形密集型應用程序的性能。介紹一下所需用到的幾個類。
BufferedGraphicsContext類提供建立圖形緩衝區的方法,該緩衝區能夠用於雙緩衝,建立用於繪製緩衝圖形的 BufferedGraphics 實例。
BufferedGraphicsManager能夠實現圖形的自定義雙緩衝,提供對應用程序域的主緩衝圖形上下文對象的訪問。此類有一個靜態屬性Current,該屬性返回當前應用程序域的主BufferGraphicsContext
BufferedGraphics類用於繪製緩衝圖形,它沒有公共的構造函數,必須有應用程序域的BufferedGraphicsContext對象使用其Allocate方法建立。提供圖形緩衝區的包裝,以及可用於寫入緩衝區和將其內容呈現到輸出設備的方法。經過Graphics屬性提供對Graphics對象的訪問(即訪問一系列的Draw方法,便可在緩衝區中進行繪製操做),Render()方法用來將圖形緩衝區的內容寫入到與緩衝區關聯的區域。
所以實如今緩衝區繪製圖形而且最終顯示到屏幕上的目標控件中可經過以下代碼:
//得到分配和管理圖形的緩衝區(該應用程序域的主緩衝區)
BufferedGraphicsContext current = BufferedGraphicsManager.Current;
//建立用於緩衝區繪製的對象myBuffer,像素格式爲pictureBox.CreateGraphics,大小和pictureBox一致
//這裏咱們假設緩衝區圖像最終是顯示到pictureBox中的
BufferedGraphics myBuffer = current.Allocate(pictureBox.CreateGraphics(),pictureBox.DisplayReactangle);
//和以前的想法一致,DrawImage用以消除繪製過程當中的多餘軌跡,DrawLine進行繪製操做(以直線爲例)
myBuffer.Graphics.DrawImage(_bitmapTemp,0,0);
myBuffer.Graphics.DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y);
//將緩衝區的圖片綁定至pictureBox,最終顯示在屏幕上
myBuffer.Render(pictureBox.CreateGraphics());
//釋放系統資源
myBuffer.Dispose();