天天看点

分组视图

当数据项数量较大的时候,为了让用户能够快速找到要查看的项,应该对数据进行分组。ItemsControl控件公开GroupStyle属性,用来设置分组视图的样式,它是一个GroupStyle对象列表,列表中的每个GroupStyle对象代表一个层次的分组。如果列表中只有一个GroupStyle对象,则表示分组视图只呈现顶层分组;如果列表中有两个GroupStyle对象,则分组视图将呈现两个层次的分组。一般来说,只需要一层分组就可以了,分组层次过多会导致应用程序界面混乱,对用户体验造成负面影响。

GroupStyle所设置的分组样式的目标类型为GroupItem,GroupItem是内容控件,派生自ContentControl,作用是在集合控件中显示分组视图,每个GroupItem实例将呈现一个组的数据。有关GroupStyle类的各个属性的详细信息参考如下:

  • HeaderTemplate 定义用于显示分组标头内容的数据模板,即如何显示每个分组的组标题
  • HeaderContainerStyle 定义组标头的容器控件的样式
  • ContainerStyle 定义GroupItem控件的样式
  • Panel 指定一个面板,用于排列每个分组视图

要实现分组视图,还需用到ColelctionViewSource类,该类可以对数据集合进行管理,尤其是支持数据分组,通过Source属性来引用原始数据列表。如果原始的数据列表已经进行过分组,应当将IsSourceGrouped属性设置为true。通常,原始数据可以使用Linq技术进行分组,并把分组结果赋值给Source属性。

CollectionViewSource类的View属性将返回一个实现了ICollectionView接口的对象实例,但该实现类并没有对外公开,开发者无法在代码中访问它,不过可以使用ICollectionView接口来访问。当数据视图分组后,IColelctionView的CollectionGroups将返回一个对象列表,其中每个对象表示一个分组的视图对象,该对象的类型实现了ICollectionViewGroup接口。同理,ICollectionViewGroup接口的实现类也没有对外公开,开发者只能通过ICollectionViewGroup接口来访问该对象。

ICollectionViewGroup接口的Group属性就是数据的分组依据,假设数据是按首字母进行分组的,那么Group属性的值就是标识该分组的单个字母。GroupItems属性返回的是属于该分组的数据项列表。

接下来将通过一个示例来演示数据视图分组功能。

本例假设有一个数据列表,表示一系列新闻纪录,并将数据列表按新闻分类进行分组(如社会新闻、经济新闻等),最终呈现数据的分组视图。每一条新闻记录将使用News类来分装,News类定义如下:

class News
    {
        ///<summary>
        ///分类
        ///</summary>
        public string Category { get; set; }
        ///<summary>
        ///标题
        ///</summary>
        public string Title { get; set; }
        ///<summary>
        ///发布日期
        ///</summary>
        public DateTime PublishDate { get; set; }
        ///<summary>
        ///内容
        ///</summary>
        public string Content { get; set; }
    }
           

随后在页面的资源中声明一个CollectionViewSource实例,稍后ListView控件将绑定到该CollectionViewSource实例。

<Page.Resources>
        <CollectionViewSource x:Key="cvs" x:Name="cvs" IsSourceGrouped="True"/>
    </Page.Resources>
           

为资源对象设置x:Key名称是为了方便在XAML文档的其他位置进行引用,而设置x:Name值是为了随后能够在代码中访问该CollectionViewSource实例。

在用户界面上声明ListView实例,并自定义它的子项目数据模板,以及用于呈现分组视图的GroupStyle。XAML代码如下:

<Grid>
        <ListView SelectionMode="None" ItemsSource="{Binding Source={StaticResource cvs}}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="0,5">
                        <TextBlock FontWeight="Bold" FontSize="24" Text="{Binding Path=Title}"/>
                        <TextBlock Text="{Binding Path=Content}" FontSize="20"/>
                        <TextBlock Opacity="0.85">
                            发布日期: 
                            <Run Text="{Binding Path=PublishDate}"/>
                        </TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
            <ListView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <TextBlock FontSize="32" FontWeight="Bold" Text="{Binding Path=Key}"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.HeaderContainerStyle>
                        <Style TargetType="ListViewHeaderItem">
                            <Setter Property="Background" Value="Red"/>
                            <Setter Property="Foreground" Value="White"/>
                            <Setter Property="Padding" Value="2,5"/>
                        </Style>
                    </GroupStyle.HeaderContainerStyle>
                </GroupStyle>
            </ListView.GroupStyle>
        </ListView>
    </Grid>
           

ListView控件的ItemsSource属性引用资源中的CollectionViewSource实例,这样就可以让ListView控件以该CollectionViewSource实例为数据源。

GroupStyley用于设置分组视图项的样式,在本例中,通过HeadCotainerStyle属性设置用于显示分组标头区域的控件的样式,以及通过HeadTemplate属性来定义分组标头的内容模板。

切换到代码视图,在页面类的构造方法中,为CollectionViewSource对象提供一些示例数据:

public MainPage()
        {
            this.InitializeComponent();
            List<News> newsList = new List<News>();
            for (int i = 0; i <=4; i++)
            {
                newsList.Add(new News { 
                    Category="社会",
                    Title="示例新闻"+i.ToString(),
                    Content="测试新闻内容"+i.ToString(),
                    PublishDate=DateTime.Now.AddDays(i)
                    });
            }
            for (int i = 0; i <=4; i++)
            {
                newsList.Add(new News
                {
                    Category = "娱乐",
                    Title = "示例新闻" + i.ToString(),
                    Content = "测试新闻内容" + i.ToString(),
                    PublishDate = DateTime.Now.AddDays(i)
                });
            }
            for (int i = 0; i <= 4; i++)
            {
                newsList.Add(new News
                {
                    Category = "法则",
                    Title = "示例新闻" + i.ToString(),
                    Content = "测试新闻内容" + i.ToString(),
                    PublishDate = DateTime.Now.AddDays(i)
                });
            }
            //对数据进行分组
            var groups = from n in newsList
                         group n by n.Category;
            //设置数据源
            cvs.Source = groups;
        }
           

上面代码中,使用以下Linq语句将示例数据进行分组:

var groups = from n in newsList
                         group n by n.Category;
           

分组的依据是News对象的Category属性。在数据分组后,Linq语句的查询结果为IGrouping<TKey,TElement>类型列表,表示已分组的一组数据列表,其中Key属性表示该分组的依据项(即组标题)。所以前面在XAML中定义GroupStyle的组标头数据模板时,里面的TextBlock控件的Text属性所绑定的就是IGrouping<TKey,TElement>对象的Key属性,以显示分组的组标题。

<TextBlock FontSize="32" FontWeight="Bold" Text="{Binding Path=Key}"/>
           

运行应用程序,会看到如下图所示的结果。

分组视图