1. 程式人生 > >C#控制元件改變大小時閃爍問題

C#控制元件改變大小時閃爍問題

首先是一段窗體控制元件隨窗體大小改變程式碼,但是在使用這段程式碼後,當窗體中控制元件較多時每次改變窗體大小後都會出現閃爍情況

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);
}​

問題得到解決