[WPF] 用自定义Panel更好地处理Resize时的行为
1. Panel里的Item之间有一个Margin。但是要对最后一个或第一个Item特别处理:它不需要这个Margin。
2. 窗口在Resize的过程中,Item之间会出现彼此遮挡或是出现某个Item只是部分可见。这时我们更希望这个Item干脆隐藏起来好了。
3. 很难在运行时调整Item的顺序。(注意这讨论的是Panel不是ItemsControl,这个问题对于后者很容易,直接控制后台绑定的数据就可以了)
4. 让Panel内Item的大小根据Panel的大小自动调节。
上面这些问题,在实现项目中时常会遇到,但是WPF没有提供一个专门的Panel来完美地处理所有这些问题。下面先具体介绍一下这几个问题。
第1个问题太常见了,就不多解释了。
第2个问题,一个Item,如果地方不够完全显示出来,那就不要显示了嘛。显示出来个半成品多难看,还不如不显示。本来想做一个AutoHideBehavior,结果发现不是那么容易,因为一个控件是否显示是由其父控件控制的,而Arrange的过程又没有定义什么Event可以控制。最好的方式还是用一个Custom Panel来实现。WPF已经自带一个类似这样的Panel叫ToolbarPanel。这个Panel真是自扫门前雪啊,实现了Toolbar上所需要的功能就完工,想在别的地方用,行为还配不上,只能自己做一个。
对于问题4,TabControl的Header就是一个例子,WPF专门做一个TabPanel来控制Tab Header的布局,当Tab的Header有两排时,这些Header会自动填满Header的宽。很纳闷MS就不能做一个叫AutoFillWrapPanel之类的通用一些的Panel,或是给WrapPanel提供一个AutoFill属性吗?这个功能也可以通过为每个Child都定义好MaxWidth和MinWidth属性,但是这属于一种hard code,一种magic number。
有不满才有进步嘛。MS要是什么功能都做了,那些控件开发商不是都要倒了?
这里就基于StackPanel的基本行为,加入一些扩展功能。包括:
1. ItemMargin:控制Panel里Item之间的间距
2. Orientation:控制Panel里Item的放置方向。StackPanel的功能。
3. ItemExtraWidth:当Panel足够宽时,给里面每个Item多分配的最大宽度。
4. ItemExtraHeight:当Panel足够高时,给里面每个Item多分配的最大高度。
5. LastVisualChildFill:显示中,最后一个Item是否填充Panel的剩余空间。
6. IsParticalItemHidden:当Panel的剩余空间不足以完整地显示当前Item时,就不显示。
7. ArrangeManager.ArrangeIndex:控制Panel的Children的渲染顺序。默认就是定义顺序。
8. Resizers:控制Panel在Resize时的行为。
这些属性还是比较抽象的,我们来具体看一下这个Panel在被Resize时的行为。
图1. Resize时的行为(最大)
图2. Resize时的行为。(缩小ItemMargin)
图3. Resize时的行为。(缩小Item的附加宽度)
图4. Resize时的行为。(隐藏不完全可见对象)
而这个自定义Panel的默认行为与StackPanel是一样的。在上面的示例中,是先缩小了Item之间的间隔再缩小Item本身。可能有的人不想要这样的行为。这个扩展的StackPanel也提供了一个Resizers属性来控制这个行为。代码如下:
<r:ExtraLengthResizer IsEnabled="True"/>
<r:ItemMarginResizer IsEnabled="True"/>
</r:ResizerCollection>
含义也一目了然。在这个ResizerCollection里,不仅可以控制哪个Resize行为是否开启。还可以控制各个Resize行为的顺序。在上面的例子中,就会先对Item的大小进行Resize。
自己感觉这个控制Resize行为的设计并不优美,但是一时又想不出来更好的方案。只当是抛砖引玉吧。完整的程序可以从这里下载到。