C#畫圖閃爍問題

來源:互聯網
上載者:User

導致畫面閃爍的關鍵原因分析:
一、繪製視窗由於大小位置狀態改變進行重繪操作時
繪圖視窗內容或大小每改變一次,都要調用Paint事件進行重繪操作,該操作會使畫面重新重新整理一次以維持視窗正常顯示。重新整理過程中會導致所有圖元重新繪製,而各個圖元的重繪操作並不會導致Paint事件發生,因此視窗的每一次重新整理只會調用Paint事件一次。視窗重新整理一次的過程中,每一個圖元的重繪都會立即顯示到視窗,因此整個視窗中,只要是圖元所在的位置,都在重新整理,而重新整理的時間是有差別的,閃爍現象自然會出現。
所以說,此時導致視窗閃爍現象的關鍵因素並不在於Paint事件調用的次數多少,而在於各個圖元的重繪。
根據以上分析可知,當圖元數目不多時,視窗重新整理的位置也不多,視窗閃爍效果並不嚴重;當圖元數目較多時,繪圖視窗進行重繪的圖元數量增加,繪圖視窗每一次重新整理都會導致較多的圖元重新繪製,視窗的較多位置都在重新整理,閃爍現象自然就會越來越嚴重。特別是圖元比較大繪製時間比較長時,閃爍問題會更加嚴重,因為時間延遲會更長。
解決上述問題的關鍵在於:視窗重新整理一次的過程中,讓所有圖元同時顯示到視窗。
二、進行滑鼠跟蹤繪製操作或者對圖元進行變形操作時
當進行滑鼠跟蹤繪製操作或者對圖元進行變形操作時,Paint事件會頻繁發生,這會使視窗的重新整理次數大大增加。雖然視窗重新整理一次的過程中所有圖元同時顯示到視窗,但也會有時間延遲,因為此時視窗重新整理的時間間隔遠小於圖元每一次顯示到視窗所用的時間。因此閃爍現象並不能完全消除!
所以說,此時導致視窗閃爍現象的關鍵因素在於Paint事件發生的次數多少。
解決此問題的關鍵在於:設定表單或控制項的幾個關鍵屬性。

解決雙緩衝的關鍵技術:
1、設定顯示圖元控制項的幾個屬性: 必須要設定,否則效果不是很明顯!
this.SetStyle(ControlStyles.OptimizedDoubleBuffer |   
ControlStyles.ResizeRedraw |
ControlStyles.AllPaintingInWmPaint, true);
2、視窗重新整理一次的過程中,讓所有圖元同時顯示到視窗。
可以通過以下幾種方式實現,這幾種方式都涉及到Graphics對象的建立方式。
Graphics對象的建立方式:
a、在記憶體上建立一塊和顯示控制項相同大小的畫布,在這塊畫布上建立Graphics對象。
接著所有的圖元都在這塊畫布上繪製,繪製完成以後再使用該畫布覆蓋顯示控制項的背景,從而達到“顯示一次僅重新整理一次”的效果!
  實現代碼(在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; //高品質
g.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素位移品質
  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)//僅當編輯節點操作時顯示圖元熱點
{
drawobject.DrawTracker(g);
 }
}

}
    using (Graphics tg = e.Graphics)
{
tg.DrawImage(bufferimage, 0, 0);  //把畫布貼到畫面上
}
b、直接在記憶體上建立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)//僅當編輯節點操作時顯示圖元熱點
{
drawobject.DrawTracker(g);
 }
}
}
    myBuffer.Render(e.Graphics);
g.Dispose();
myBuffer.Dispose();//釋放資源
至此,雙緩衝問題解決,兩種方式的實現效果都一樣,但最後一種方式的佔有的記憶體很少,不會出現記憶體泄露!

或者:
1、在記憶體中建立一塊“虛擬畫布”:

Bitmap bmp = new Bitmap(600, 600);

  2、擷取這塊記憶體畫布的Graphics引用:

Graphics g = Graphics.FromImage(bmp);

  3、在這塊記憶體畫布上繪圖:

g.FillEllipse(brush, i * 10, j * 10, 10, 10);

  4、將記憶體畫布畫到視窗中

this.CreateGraphics().DrawImage(bmp, 0, 0);

還有的方式
在建構函式中加如下代碼

代碼一:
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
SetStyle(ControlStyles.DoubleBuffer, true); // 雙緩衝

代碼二:
this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.