C#雙緩沖技術(shù)實例詳解
本文實例分析了C#雙緩沖技術(shù)。分享給大家供大家參考,具體如下:
雙緩沖解決閃爍問題。
整理:
GDI+的雙緩沖問題
一直以來的誤區(qū):.net1.1 和 .net 2.0 在處理控件雙緩沖上是有區(qū)別的。
.net 1.1 中,使用:this.SetStyle(ControlStyles.DoubleBuffer, true);
.net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
導致畫面閃爍的關(guān)鍵原因分析:
一、繪制窗口由于大小位置狀態(tài)改變進行重繪操作時
繪圖窗口內(nèi)容或大小每改變一次,都要調(diào)用Paint事件進行重繪操作,該操作會使畫面重新刷新一次以維持窗口正常顯示。刷新過程中會導致所有圖元重新繪制,而各個圖元的重繪操作并不會導致Paint事件發(fā)生,因此窗口的每一次刷新只會調(diào)用Paint事件一次。窗口刷新一次的過程中,每一個圖元的重繪都會立即顯示到窗口,因此整個窗口中,只要是圖元所在的位置,都在刷新,而刷新的時間是有差別的,閃爍現(xiàn)象自然會出現(xiàn)。
所以說,此時導致窗口閃爍現(xiàn)象的關(guān)鍵因素并不在于Paint事件調(diào)用的次數(shù)多少,而在于各個圖元的重繪。
根據(jù)以上分析可知,當圖元數(shù)目不多時,窗口刷新的位置也不多,窗口閃爍效果并不嚴重;當圖元數(shù)目較多時,繪圖窗口進行重繪的圖元數(shù)量增加,繪圖窗口每一次刷新都會導致較多的圖元重新繪制,窗口的較多位置都在刷新,閃爍現(xiàn)象自然就會越來越嚴重。特別是圖元比較大繪制時間比較長時,閃爍問題會更加嚴重,因為時間延遲會更長。
解決上述問題的關(guān)鍵在于:窗口刷新一次的過程中,讓所有圖元同時顯示到窗口。
二、進行鼠標跟蹤繪制操作或者對圖元進行變形操作時
當進行鼠標跟蹤繪制操作或者對圖元進行變形操作時,Paint事件會頻繁發(fā)生,這會使窗口的刷新次數(shù)大大增加。雖然窗口刷新一次的過程中所有圖元同時顯示到窗口,但也會有時間延遲,因為此時窗口刷新的時間間隔遠小于圖元每一次顯示到窗口所用的時間。因此閃爍現(xiàn)象并不能完全消除!
所以說,此時導致窗口閃爍現(xiàn)象的關(guān)鍵因素在于Paint事件發(fā)生的次數(shù)多少。
解決此問題的關(guān)鍵在于:設置窗體或控件的幾個關(guān)鍵屬性。
使用雙緩沖
解決雙緩沖的關(guān)鍵技術(shù):
1、設置顯示圖元控件的幾個屬性: 必須要設置,否則效果不是很明顯!
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true);
2、窗口刷新一次的過程中,讓所有圖元同時顯示到窗口。
可以通過以下幾種方式實現(xiàn),這幾種方式都涉及到Graphics對象的創(chuàng)建方式。
具體實現(xiàn)
1、利用默認雙緩沖
(1)在應用程序中使用雙緩沖的最簡便的方法是使用 .NET Framework 為窗體和控件提供的默認雙緩沖。通過將 DoubleBuffered 屬性設置為 true。
this.DoubleBuffered=true;
(2)使用 SetStyle 方法可以為 Windows 窗體和所創(chuàng)作的 Windows 控件啟用默認雙緩沖。
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
2、手工設置雙緩沖
.netframework提供了一個類BufferedGraphicsContext負責單獨分配和管理圖形緩沖區(qū)。每個應用程序域都有自己的默認 BufferedGraphicsContext 實例來管理此應用程序的所有默認雙緩沖。大多數(shù)情況下,每個應用程序只有一個應用程序域,所以每個應用程序通常只有一個默認 BufferedGraphicsContext。默認 BufferedGraphicsContext 實例由 BufferedGraphicsManager 類管理。通過管理BufferedGraphicsContext實現(xiàn)雙緩沖的步驟如下:
(1)獲得對 BufferedGraphicsContext 類的實例的引用。
(2)通過調(diào)用 BufferedGraphicsContext.Allocate 方法創(chuàng)建 BufferedGraphics 類的實例。
(3)通過設置 BufferedGraphics.Graphics 屬性將圖形繪制到圖形緩沖區(qū)。
(4)當完成所有圖形緩沖區(qū)中的繪制操作時,可調(diào)用 BufferedGraphics.Render 方法將緩沖區(qū)的內(nèi)容呈現(xiàn)到與該緩沖區(qū)關(guān)聯(lián)的繪圖圖面或者指定的繪圖圖面。
(5)完成呈現(xiàn)圖形之后,對 BufferedGraphics 實例調(diào)用釋放系統(tǒng)資源的 Dispose 方法。
完整的例子,在一個400*400的矩形框內(nèi)繪制10000個隨機生成的小圓。
BufferedGraphicsContext current = BufferedGraphicsManager.Current; //(1) BufferedGraphics bg; bg = current.Allocate(this.CreateGraphics(),this.DisplayRectangle); //(2) Graphics g = bg.Graphics;//(3) //隨機 寬400 高400 System.Random rnd = new Random(); int x,y,w,h,r,i; for (i = 0; i < 10000; i++) { x = rnd.Next(400); y = rnd.Next(400); r = rnd.Next(20); w = rnd.Next(10); h = rnd.Next(10); g.DrawEllipse(Pens.Blue, x, y, w, h); } bg.Render();//(4) //bg.Render(this.CreateGraphics()); bg.Dispose();//(5)
3、自己開辟一個緩沖區(qū)(如一個不顯示的Bitmap對象),在其中繪制完成后,再一次性顯示。
完整代碼如下:
Bitmap bt = new Bitmap(400, 400); Graphics bg = Graphics.FromImage(bt); System.Random rnd = new Random(); int x, y, w, h, r, i; for (i = 0; i < 10000; i++) { x = rnd.Next(400); y = rnd.Next(400); r = rnd.Next(20); w = rnd.Next(10); h = rnd.Next(10); bg.DrawEllipse(Pens.Blue, x, y, w, h); } this.CreateGraphics().DrawImage(bt, new Point(0, 0));
另外一個例子,差不多
Graphics對象的創(chuàng)建方式:
a、在內(nèi)存上創(chuàng)建一塊和顯示控件相同大小的畫布,在這塊畫布上創(chuàng)建Graphics對象。
接著所有的圖元都在這塊畫布上繪制,繪制完成以后再使用該畫布覆蓋顯示控件的背景,從而達到“顯示一次僅刷新一次”的效果!
實現(xiàn)代碼(在OnPaint方法中):
Rectangle rect = e.ClipRectangle; Bitmap bufferimage = new Bitmap(this.Width, this.Height); Graphics g = Graphics.FromImage(bufferimage); g.Clear(this.BackColor); g.SmoothingMode = SmoothingMode.HighQuality; //高質(zhì)量 g.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素偏移質(zhì)量 foreach (IShape drawobject in doc.drawObjectList) { if (rect.IntersectsWith(drawobject.Rect)) { drawobject.Draw(g); if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected && this.CurrentOperator == Enum.Operator.Transfrom)//僅當編輯節(jié)點操作時顯示圖元熱點 { drawobject.DrawTracker(g); } } } using (Graphics tg = e.Graphics) { tg.DrawImage(bufferimage, 0, 0);//把畫布貼到畫面上 }
b、直接在內(nèi)存上創(chuàng)建Graphics對象:
Rectangle rect = e.ClipRectangle; BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current; BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics, e.ClipRectangle); Graphics g = myBuffer.Graphics; g.SmoothingMode = SmoothingMode.HighQuality; g.PixelOffsetMode = PixelOffsetMode.HighSpeed; g.Clear(this.BackColor); foreach (IShape drawobject in doc.drawObjectList) { if (rect.IntersectsWith(drawobject.Rect)) { drawobject.Draw(g); if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected && this.CurrentOperator == Enum.Operator.Transfrom)//僅當編輯節(jié)點操作時顯示圖元熱點 { drawobject.DrawTracker(g); } } } myBuffer.Render(e.Graphics); g.Dispose(); myBuffer.Dispose();//釋放資源
至此,雙緩沖問題解決,兩種方式的實現(xiàn)效果都一樣,但最后一種方式的占有的內(nèi)存很少,不會出現(xiàn)內(nèi)存泄露!
接下來是對acdsee拖動圖片效果的實現(xiàn)。開始不懂雙緩沖,以為雙緩沖可以解決這個問題,結(jié)果發(fā)現(xiàn)使用了雙緩沖沒啥效果,請教了高人,然后修改了些代碼,完成這個效果。
圖片是在pictureBox1里。
Bitmap currentMap; bool first = true; private void pictureBox1_MouseDown(object sender, MouseEventArgs e) { if (zoom == 0) { if (e.Button == MouseButtons.Left) //dragging mousedrag = e.Location; Image myImage = myMap.GetMap(); currentMap = new Bitmap(myImage); first = false; } } private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { if (zoom == 0&&!first) { Image img = new Bitmap(Size.Width, Size.Height); Graphics g = Graphics.FromImage(img); g.Clear(Color.Transparent);//圖片移動后顯示的底色 g.SmoothingMode = SmoothingMode.HighQuality; //高質(zhì)量 g.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素偏移質(zhì)量 g.DrawImageUnscaled(currentMap, new System.Drawing.Point(e.Location.X - mousedrag.X, e.Location.Y - mousedrag.Y));//在g中移動圖片,原圖在(0,0)畫的,所以直接用new System.Drawing.Point(e.Location.X - mousedrag.X, e.Location.Y - mousedrag.Y)就好。 g.Dispose(); pictureBox1.Image = img;//img是在鼠標這個位置時生成被移動后的暫時的圖片 } } private void pictureBox1_MouseUp(object sender, MouseEventArgs e) { if (zoom == 0) { System.Drawing.Point pnt = new System.Drawing.Point(Width / 2 + (mousedrag.X - e.Location.X), Height / 2 + (mousedrag.Y - e.Location.Y)); myMap.Center = myMap.ImageToWorld(pnt); pictureBox1.Image = myMap.GetMap(); first = true; } }
說說思路,在鼠標點下時創(chuàng)建一個bitmap,currentMap,用它來存放當前圖像。鼠標移動時,根據(jù)鼠標位置畫圖,最后,鼠標up時,重新畫圖。
更多關(guān)于C#相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《C#面向?qū)ο蟪绦蛟O計入門教程》、《C#常見控件用法教程》及《C#數(shù)據(jù)結(jié)構(gòu)與算法教程》
希望本文所述對大家C#程序設計有所幫助。
相關(guān)文章
C#將HashTable中鍵列表或值列表復制到一維數(shù)組的方法
這篇文章主要介紹了C#將HashTable中鍵列表或值列表復制到一維數(shù)組中方法,涉及C#操作HashTable的相關(guān)技巧,需要的朋友可以參考下2015-04-04C#獲取計算機硬件與操作系統(tǒng)的相關(guān)信息
這篇文章介紹了C#獲取計算機硬件與操作系統(tǒng)相關(guān)信息的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04C#中把字符串String轉(zhuǎn)換為整型Int的小例子
這篇文章主要介紹了C#中把字符串String轉(zhuǎn)換為整型Int的小例子,本文使用TryParse方法實現(xiàn)轉(zhuǎn)換,需要的朋友可以參考下2014-08-08