无论是 .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 事件的组合,这两个事件都只会触发一次,所以不会产生不成对的问题。