當時並沒有想太多,因為該專案不算複雜,我只定義了一種叫 ItemBase 的類別來讓專案中所有的 GridView 做 Binding,直到後來我碰到一個狀況。
在最近我發現之前的寫法有個很大的問題,我每定義一種結構,就要為了那種結構生一個專門處理它的衍生自 GridView 的類別,原因在於 PrepareContainerForItemOverride 這個 Method 裡面我必須把 Item Object 做轉型才能拿到 Span 資訊,所以後來我定義了一個介面名為 ISpanItem 長下面這個樣子
public interface ISpanItem
{
    int ItemRowSpan {  get; }
    int ItemColumnSpan {  get; }
}
接著我只要讓所有的結構實作 ISpanItem 就可以利用同一個 GridView 做出 VariableSizedWrapGrid 效果,一開始我是這麼寫的
class NoSpanItemSizeException : Exception
{
    public NoSpanItemSizeException() : base("must implement ISpanItem this interface") { }
}
 
public interface ISpanItem
{
    int ItemRowSpan {  get; }
    int ItemColumnSpan {  get; }
}
 
public class VSWGridView : GridView
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
 
        ISpanItem _spanItem = null;
        try
        {
            _spanItem = (ISpanItem)item;
        }
        catch (Exception e) // 這個地方若只接 InvalidCastException 會更好
        {
            throw new NoSpanItemSizeException();
        }
 
        VariableSizedWrapGrid.SetRowSpan(element as UIElement, _spanItem.ItemRowSpan);
        VariableSizedWrapGrid.SetColumnSpan(element as UIElement, _spanItem.ItemColumnSpan);
    }
}
其實這樣的寫法沒什麼問題,只是我後來在想,真的有必要用一個 try catch 來避免非 ISpanItem 的介面嗎?
使用 (型別)物件 這種強制轉型的語法也一直是我的習慣,它的確只能 catch Exception 這種方式來知道轉型失敗,後來我查了一下,看來還有更好的寫法,所以我把這段程式碼改成這個樣子
public class VSWGridView : GridView
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
 
        int rowSpan = 1;
        int colSpan = 1;
 
        if (item is ISpanItem)
        {
            ISpanItem si = item as ISpanItem;
            rowSpan = si.ItemRowSpan;
            colSpan = si.ItemColumnSpan;
        }
 
        VariableSizedWrapGrid.SetRowSpan(element as UIElement, rowSpan);
        VariableSizedWrapGrid.SetColumnSpan(element as UIElement, colSpan);
    }
}
利用 is 來判斷該物件是否屬於該介面或該類型,並用 as 來做轉型,利用 as 來做轉型有個好處,它不會發生 Exception,所以對效能是有幫助的,若無法轉型的情況 si 就會是 null,所以我們可以判斷 si 在轉型後是否為 null 或像上面的範例一樣一開始就用 is 來判斷即可,此處我也把 throw Exception 遺棄了,讓 VSWGridView 可以通吃所有的結構,只是在沒有實作 ISpanItem 介面的狀況下就讓 VariableSize 失效如此而已。
 
沒有留言:
張貼留言