無論是 .net framework 自帶還是第三方組件,使用 Split 類控件時通常其 Panel 中都會包含多個子控件,在運行時不可避免遇到因改變 splitter 位置或改變窗體大小引起的界面重繪,此時都會希望自己的程序運行的又快又穩,前兩天就這個問題在 Q 群和 MSDN 論壇上提出了疑問,並很快得到解答,下面將經驗做個分享。
先說一開始寫的不對的地方,開始我使用的是 SplitterMoving 和 SplitterMoved 這兩個事件,這兩個事件 .net 自帶的 splitContainer 和 DevExpress 的 splitContainerControl 都有。在 SplitterMoving 中將兩個 Panel 做“掛起”—— splitContainerControl.Panel1.SuspendLayout(),並在 SplitterMoved 事件中將 Panel 恢復—— splitContainerControl.Panel1.ResumeLayout(true),興奮的去測試,結果發生了尷尬的事情:窗體運行后,先進行一次 splitter 位置的改變,然后最大化窗體,Panel 中的子控件仍然保持最大化之前的大小,就像 Dock.Fill 失靈了一樣。

改變一次 splitter 位置,並將窗體最大化后,空白出現了

馬上查資料,百度一通,都是講 SuspendLayout 可以干嘛的,沒有發現有談到這種問題的,求助於 MSDN 論壇獲得解答:SuspendLayout 和 ResumeLayout 是“成對”產生作用,即 Suspend 一次就需要 Resume 一次。在圖中可以看到,拖動 splitter 將觸發 N 次的 SplitterMoving 事件,也就是產生了 N 次 Panel1.SuspendLayout(),但 Panel1.ResumeLayout 只有一次,造成了恢復布局的失敗。
兩種解決方式:
第一、用代碼解決,定義一個變量放入事件,使 SuspendLayout 只執行一次
1 bool _suspend = false; 2 private void splitContainer1_SplitterMoving(object sender, SplitterCancelEventArgs e) 3 { 4 if (!this._suspend) 5 { 6 splitContainer1.SuspendLayout(); 7 this._suspend = true; 8 } 9 } 10 11 private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e) 12 { 13 this._suspend = false; 14 splitContainer1.ResumeLayout(); 15 }
第二、在 DevExpress 的控件里使用其獨有的 BeginSplitterMoving 和 SplitterPositionChanged 事件的組合,這兩個事件都只會觸發一次,所以不會產生不成對的問題。
