C#控制元件改變大小時閃爍問題
阿新 • • 發佈:2019-01-06
首先是一段窗體控制元件隨窗體大小改變程式碼,但是在使用這段程式碼後,當窗體中控制元件較多時每次改變窗體大小後都會出現閃爍情況
private float X; private float Y; private void setTag(Control cons) { foreach (Control con in cons.Controls) { con.Tag = con.Width +":" + con.Height + ":" + con.Left + ":" + con.Top + ":" + con.Font.Size; if (con.Controls.Count > 0) setTag(con); } } private void setControls(float newx, float newy, Control cons) { foreach (Control con in cons .Controls ) { string[] mytag = con.Tag.ToString().Split(new char[] { ':' }); float a = Convert.ToSingle(mytag[0]) * newx; con.Width = (int)a; a=Convert.ToSingle(mytag[1]) * newy; con.Height = (int)(a); a=Convert.ToSingle(mytag[2]) * newx; con.Left = (int)(a); a=Convert.ToSingle(mytag[3]) * newy; con.Top = (int)(a); Single currentSize = Convert.ToSingle (mytag[4]) * Math.Min(newx,newy); con .Font =new Font (con.Font .Name ,currentSize,con.Font .Style ,con.Font .Unit ); if(con.Controls .Count >0) { setControls (newx ,newy ,con ); } } } void Form1_Resize(object sender, EventArgs e) { float newx = (this.Width )/ X; float newy = this.Height / Y; setControls(newx, newy, this); this.Text = this.Width.ToString() +" "+ this.Height.ToString(); } 在Form_Load裡面新增: this.Resize += new EventHandler(Form1_Resize); //X = this.Width; //Y = this.Height; setTag (this); Form1_Resize(new object(),new EventArgs());//x,y可在例項化時賦值,最後這句是新加的,在MDI時有用
當控制元件比較多時,每次改變窗體大小都會有明顯的閃爍
為什麼會閃爍?
因為窗體控制元件狀態轉換時,windows需要負責"擦除"其背景,重新繪製,在一臺效能並不優良的終端上(很大可能程度上客戶端電腦都不是那麼強勁吧) ,這個過程不是一時半會就能完成的,尤其對於很多個子控制元件的情況,因此就…
查詢一些資料提到的解決方法有:
1.雙緩衝
SetStyle(ControlStyles.DoubleBuffer, true);
效果不是很明顯
2.由於底層重繪每次會清除畫布,然後再全部重新繪製,這才是導致閃爍最主要的原因。於是過載訊息傳送函式操作,禁掉這條訊息。程式碼如下:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0014) // 禁掉清除背景訊息
return;
base.WndProc(ref m);
}
但是這樣效果依然不明顯
繼續研究,看到網上有人寫過解決類似問題方法,其原理是參考ListView控制元件中的兩個函式
internal void BeginUpdateInternal() { if (this.IsHandleCreated) { if (this.updateCount == 0) { this.SendMessage(11, 0, 0); } this.updateCount = (short) (this.updateCount + 1); } }
this.SendMessage(11, 0, 0)表示它給自身Send了一個code為11的windows訊息,
在windows訊息定義中可以看到 WM_SETREDRAW = 0x0B (0x0B也就是11),這行程式碼的意思是告訴windows對ListView控制元件停止重繪介面,直到顯式要求重新繪製為止.
internal bool EndUpdateInternal(bool invalidate)
{
if (this.updateCount <= 0)
{
return false;
}
this.updateCount = (short) (this.updateCount - 1);
if (this.updateCount == 0)
{
this.SendMessage(11, -1, 0);
if (invalidate)
{
this.Invalidate();
}
}
return true;
}
同樣有一行程式碼: this.SendMessage(11, –1, 0); 11還是同一個意思,此時告知windows可以重繪ListView控制元件了
按照這種思路在Resize函式中增加發送訊息
[DllImport("user32")]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, IntPtr lParam);
private const int WM_SETREDRAW = 0xB;
private void Form2_Resize(object sender, EventArgs e)
{
SendMessage(this.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
float newx = (this.Width) / X;
float newy = this.Height / Y;
setControls(newx, newy, this);
SendMessage(this.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
this.Invalidate(true);
}
問題得到解決