• 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>

    到这,目标达到了,即实现了不规这宫格布局,还实现了不同的宫格对应不同的模版。
    源代码下载

  • 相关阅读:
    部分类Partial
    Ajax之XMLHttpRequest
    C#结构(Struct)
    sql数据管理语句
    mysql基本语句
    bootstrap插件-滚动监听
    一轮项目学习总结
    js滚动监听
    Ajax
    各框架、插件记录
  • 原文地址:https://www.cnblogs.com/dagehaoshuang/p/2651064.html
Copyright © 2020-2023  润新知