WPF內建的Pane很多,但是功能都過於基本。對於一些常見功能的支援都不是很方便。下面的一些情況應該有不少人都遇到過。
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:ResizerCollection>
<r:ExtraLengthResizer IsEnabled="True"/>
<r:ItemMarginResizer IsEnabled="True"/>
</r:ResizerCollection>
含義也一目瞭然。在這個ResizerCollection裡,不僅可以控制哪個Resize行為是否開啟。還可以控制各個Resize行為的順序。在上面的例子中,就會先對Item的大小進行Resize。
自己感覺這個控制Resize行為的設計並不優美,但是一時又想不出來更好的方案。只當是拋磚引玉吧。完整的程式可以從這裡下載到。