Windows 8 Metro 開發疑難雜症(二)——不規則宮格布局以及不同Item對應不同模版


我先介紹下win8里面帶有的列表控件:

  1. ItemsControl  ItemsControl  是所有列表控件的基類,實際使用過程中不常用。
  2. ListBox listbox控件在win8里面的地位已經下降了很多(在WP中Listbox是最常用的列表控件),也是屬於不常用控件。
  3. ListView listview控件的地位相當於WP中的listbox,是用來做垂直列表或水平列表最好控件,不支持宮格列表。
  4. GridView  gridview是專門用來做宮格布局的,不能用來做垂直或水平列表控件(雖然可以用StackPanel面板,但是不能滑動的)。
  5. FlipView flipview控件界面上一次只能呈現一個item,主要用來做圖片瀏覽用的。

下面說正事。

不知道大家有沒有注意過Win8自帶的商店的宮格的布局以及metro界面下桌面的宮格布局?這兩種布局方式每一格的占用大小是不一樣的。但是在win8自帶的控件中沒有這樣的控件,雖然有gridview控件,但是gridview本身不支持不規則(下面提到的不規則指的是每一個宮格占用的空間不一致)宮格布局,但是gridview 支持VariableSizedWrapGrid,而VariableSizedWrapGrid是支持不規則宮格布局的,這樣我們就可以自定義一個控件來實現以上的功能。

下面新建一個網格應用程序項目。直接運行程序會看到首頁的布局是宮格的布局,但是每一格的大小是一樣的(如下圖)。

下面我們需要自定義一個控件來實現不規則宮格布局,叫做VariableSizedGridView控件,繼承自GridView

  public class VariableSizedGridView : GridView
    {
        protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
        {
            IGridItemSize gridItemSize = item as IGridItemSize;
            ContentControl gridViewItem = element as ContentControl;
            if (gridItemSize != null)
            {
                //設置元素占用的列數和行數
                VariableSizedWrapGrid.SetColumnSpan(gridViewItem, gridItemSize.ColSpan);
                VariableSizedWrapGrid.SetRowSpan(gridViewItem, gridItemSize.RowSpan);
            }
            base.PrepareContainerForItemOverride(element, item);
        }
    }

還有定義一個接口用來實現宮格所占的列數和行數,新建類IGridItemSize,代碼如下:

 public interface IGridItemSize
    {
        //占用列的數量
        int ColSpan { get; set; }
        //占用行的數量
        int RowSpan { get; set; }
    }

為了能實現不規在布局,還要給數據源的Item繼承IGridItemSize接口,這樣才能讓VariableSizedGridView控件實現不規在布局。
修改SampleDataSource文件下的SampleDataItem類,讓SampleDataItem繼承IGridItemSize接口,在SampleDataItem構造函數里面添加如下代碼。然后在類SampleDataSource的構造函數里給SampleDataItem的ColSpan或RowSpan屬性賦值。

現在我們需要修改GroupedItemsPage的XAML代碼,如下:

     <local:VariableSizedGridView
            x:Name="itemGridView"
            AutomationProperties.AutomationId="ItemGridView"
            AutomationProperties.Name="Grouped Items"
            Grid.Row="1"
            Margin="0,-3,0,0"
            Padding="116,0,40,46"
            ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
            SelectionMode="None"
            IsItemClickEnabled="True"
            ItemClick="ItemView_ItemClick">
            <GridView.ItemContainerStyle>
                <Style TargetType="GridViewItem">
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </GridView.ItemContainerStyle>
                <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid >
                        <Border Background="PowderBlue">
                        </Border>
                        <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                            <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
                            <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
            <GridView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <Grid Margin="1,0,0,6">
                                <Button
                                    AutomationProperties.Name="Group Title"
                                    Content="{Binding Title}"
                                    Click="Header_Click"
                                    Style="{StaticResource TextButtonStyle}"/>
                            </Grid>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.Panel>
                        <ItemsPanelTemplate>
                            <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0" ItemHeight="250" ItemWidth="250"/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </GridView.GroupStyle>
        </local:VariableSizedGridView>

這樣就可以直接運行,看到效果了,如下圖:

 

到此我得提醒下各位,由於宮格的每行的高度和每列的寬度是固定,那也就是說我們需要提前計算好不規則宮格所占用的列數和行數,以便達到最佳的布局。

到這里我們可能會想,既然每一格占用的列數和行數是不一定的,那是否可以對每一格的模版進行差異化設置呢?如下圖效果:

 

答案是肯定的。這里依然需要對我們的自定義控件進行改造。

首先我們需要一個DataTemplat集合,用來存放不同的宮格對應的模版,然后我們需要一個附加屬性,對DataTemplate設置一個Tag值用來標記模版,這樣在初始化Item的時候能找到正確的模版。我們還需要給IGridItemSize添加一個類型為Int的屬性Tag。

 public class VariableSizedGridView : GridView
    {
        public VariableSizedGridView()
        {
            this.DataTypes = new List<DataTemplate>();
        }

        public static readonly DependencyProperty DataTemplateDataTagProperty = DependencyProperty.RegisterAttached("DataTemplateDataTag", typeof(int), typeof(VariableSizedGridView), new PropertyMetadata(0));

        public static int GetDataTemplateDataTag(DataTemplate element)
        {
            return (int)element.GetValue(VariableSizedGridView.DataTemplateDataTagProperty);
        }

        public static void SetDataTemplateDataTag(DataTemplate element, int value)
        {
            element.SetValue(VariableSizedGridView.DataTemplateDataTagProperty, value);
        }

        public static readonly DependencyProperty DataTypesProperty = DependencyProperty.Register("DataTypes", typeof(List<DataTemplate>), typeof(VariableSizedGridView), null);

        public List<DataTemplate> DataTypes
        {
            get
            {
                return this.GetValue(VariableSizedGridView.DataTypesProperty) as List<DataTemplate>;
            }
            set
            {
                this.SetValue(VariableSizedGridView.DataTypesProperty, value);
            }
        }

        protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
        {
            IGridItemSize gridItemSize = item as IGridItemSize;
            ContentControl gridViewItem = element as ContentControl;
            if (gridItemSize != null)
            {
                //設置元素占用的列數和行數
                VariableSizedWrapGrid.SetColumnSpan(gridViewItem, gridItemSize.ColSpan);
                VariableSizedWrapGrid.SetRowSpan(gridViewItem, gridItemSize.RowSpan);
                //如果tag不為0,那么表示需要使用默認模板以外的模版
                if (gridItemSize.Tag == 0)
                {
                    base.PrepareContainerForItemOverride(element, item);
                }
                else
                {
                    //獲取tag對應的模版
                    var a = this.DataTypes.FirstOrDefault(c => VariableSizedGridView.GetDataTemplateDataTag(c) == gridItemSize.Tag);
                    if (a == null)
                    {
                        base.PrepareContainerForItemOverride(element, item);
                    }
                    else
                    {
                        base.PrepareContainerForItemOverride(element, item);
                        //將模版賦值給gridViewItem
                        gridViewItem.ContentTemplate = a;
                    }
                }
            }
            else
                base.PrepareContainerForItemOverride(element, item);
        }
    }

到這控件的實現邏輯已經寫完了,下面就是在實際的使用。

首先還是修改數據源的Tag值,讓不同的item對應不同的模版。修改SampleDataSource文件下的代碼。

下面再修改下GroupedItemsPage的XAML代碼

   <local:VariableSizedGridView
            x:Name="itemGridView"
            AutomationProperties.AutomationId="ItemGridView"
            AutomationProperties.Name="Grouped Items"
            Grid.Row="1"
            Margin="0,-3,0,0"
            Padding="116,0,40,46"
            ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
            SelectionMode="None"
            IsItemClickEnabled="True"
            ItemClick="ItemView_ItemClick">
            <GridView.ItemContainerStyle>
                <Style TargetType="GridViewItem">
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </GridView.ItemContainerStyle>
                <GridView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
            <local:VariableSizedGridView.DataTypes>
                <DataTemplate local:VariableSizedGridView.DataTemplateDataTag="1">
                    <Grid >
                        <Border Background="Red">

                        </Border>
                        <StackPanel VerticalAlignment="Top" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                            <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
                            <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
                 <DataTemplate local:VariableSizedGridView.DataTemplateDataTag="2">
                    <Grid >
                        <Border Background="Purple">

                        </Border>
                        <StackPanel VerticalAlignment="Center" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                            <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
                            <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </local:VariableSizedGridView.DataTypes>
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid >
                        <Border Background="PowderBlue">

                        </Border>
                        <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                            <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
                            <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
            <GridView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <Grid Margin="1,0,0,6">
                                <Button
                                    AutomationProperties.Name="Group Title"
                                    Content="{Binding Title}"
                                    Click="Header_Click"
                                    Style="{StaticResource TextButtonStyle}"/>
                            </Grid>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.Panel>
                        <ItemsPanelTemplate>
                            <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0" ItemHeight="250" ItemWidth="250"/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </GridView.GroupStyle>
        </local:VariableSizedGridView>

到這,目標達到了,即實現了不規這宮格布局,還實現了不同的宮格對應不同的模版。
源代碼下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM