Windows Phone 8.1 列表控件(2):分组数据
说到 List 控件,Windows Phone 8.1 上推荐使用的是 ListView 和 GridView。
而这两个控件实在太多东西可讲了,于是分成三篇来讲:
(2)分组数据
ListView 和 GridView 的最大差别就是:ListView 是一条条依序排列的,而 GridView 则是一块块依序排列的,因此 ListView 中的一项就会占据整整一行或者一列,而 GridView 的一项只会占据它应有的大小,一行或一列中可以放置多项。
而两者在其它方面上基本一致,因此下文只对 ListView 进行介绍,GridView 其实也一样的。
分组数据(GroupingData)
分组数据也就是将数据按首字母或自定义属性进行分组,然后用户就能通过点击首字母或自定义属性达到快速定位的目的:
构建过程:
(1)准备已分好组的数据
数据的分组可分为两类,一是根据项的首字母或拼音分组,二是根据项的自身属性分组。
1)首字母或拼音分组
public static List<AlphaKeyGroup<SampleItem>> GetAlphaGroupSampleItems() { ObservableCollection<SampleItem> data = new ObservableCollection<SampleItem>(); data.Add(new SampleItem() { Title = "k1", Content = "k1", Image = "ms-appx:/Images/k1.png", Group = "Kill La Kill" }); data.Add(new SampleItem() { Title = "w2", Content = "w2", Image = "ms-appx:/Images/w2.png", Group = "Wu Yu" }); data.Add(new SampleItem() { Title = "k3", Content = "k3", Image = "ms-appx:/Images/k3.png", Group = "Kill La Kill" }); data.Add(new SampleItem() { Title = "t4", Content = "t4", Image = "ms-appx:/Images/t4.png", Group = "Tiger" }); data.Add(new SampleItem() { Title = "t5", Content = "t5", Image = "ms-appx:/Images/t5.png", Group = "Tiger" }); data.Add(new SampleItem() { Title = "x6", Content = "x6", Image = "ms-appx:/Images/x6.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x7", Content = "x7", Image = "ms-appx:/Images/x7.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x8", Content = "x8", Image = "ms-appx:/Images/x8.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x9", Content = "x9", Image = "ms-appx:/Images/x9.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x10", Content = "x10", Image = "ms-appx:/Images/x10.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x11", Content = "x11", Image = "ms-appx:/Images/x11.png", Group = "Xi De Ni Ya" }); List<AlphaKeyGroup<SampleItem>> groupData = AlphaKeyGroup<SampleItem>.CreateGroups( data, (SampleItem s) => { return s.Title; }, true); return groupData; }
AlphaKeyGroup 类为自己写的分组类:
public class AlphaKeyGroup<T> { const string GlobeGroupKey = "\uD83C\uDF10"; public string Key { get; private set; } public List<T> InternalList { get; private set; } public AlphaKeyGroup(string key) { Key = key; InternalList = new List<T>(); } private static List<AlphaKeyGroup<T>> CreateDefaultGroups(CharacterGroupings slg) { List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>(); foreach( CharacterGrouping cg in slg ) { if( cg.Label == "" ) continue; if( cg.Label == "..." ) { list.Add(new AlphaKeyGroup<T>(GlobeGroupKey)); } else { list.Add(new AlphaKeyGroup<T>(cg.Label)); } } return list; } public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, Func<T, string> keySelector, bool sort) { CharacterGroupings slg = new CharacterGroupings(); List<AlphaKeyGroup<T>> list = CreateDefaultGroups(slg); foreach( T item in items ) { int index = 0; { string label = slg.Lookup(keySelector(item)); index = list.FindIndex(alphakeygroup => (alphakeygroup.Key.Equals(label, StringComparison.CurrentCulture))); } if( index >= 0 && index < list.Count ) { list[index].InternalList.Add(item); } } if( sort ) { foreach( AlphaKeyGroup<T> group in list ) { group.InternalList.Sort((c0, c1) => { return keySelector(c0).CompareTo(keySelector(c0)); }); } } return list; } }
2)自身属性分组
public static List<KeyedList<string, SampleItem>> GetKeyGroupSampleItems() { ObservableCollection<SampleItem> data = new ObservableCollection<SampleItem>(); data.Add(new SampleItem() { Title = "k1", Content = "k1", Image = "ms-appx:/Images/k1.png", Group = "Kill La Kill" }); data.Add(new SampleItem() { Title = "w2", Content = "w2", Image = "ms-appx:/Images/w2.png", Group = "Wu Yu" }); data.Add(new SampleItem() { Title = "k3", Content = "k3", Image = "ms-appx:/Images/k3.png", Group = "Kill La Kill" }); data.Add(new SampleItem() { Title = "t4", Content = "t4", Image = "ms-appx:/Images/t4.png", Group = "Tiger" }); data.Add(new SampleItem() { Title = "t5", Content = "t5", Image = "ms-appx:/Images/t5.png", Group = "Tiger" }); data.Add(new SampleItem() { Title = "x6", Content = "x6", Image = "ms-appx:/Images/x6.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x7", Content = "x7", Image = "ms-appx:/Images/x7.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x8", Content = "x8", Image = "ms-appx:/Images/x8.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x9", Content = "x9", Image = "ms-appx:/Images/x9.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x10", Content = "x10", Image = "ms-appx:/Images/x10.png", Group = "Xi De Ni Ya" }); data.Add(new SampleItem() { Title = "x11", Content = "x11", Image = "ms-appx:/Images/x11.png", Group = "Xi De Ni Ya" }); var groupedItems = from item in data orderby item.Group group item by item.Group into itemsByGroup select new KeyedList<string, SampleItem>(itemsByGroup); return groupedItems.ToList(); }
KeyedList 为自己编写的类:
public class KeyedList<TKey, TItem>: List<TItem> { public TKey Key { protected set; get; } public IEnumerable<TItem> InternalList { protected set; get; } public KeyedList(TKey key, IEnumerable<TItem> items) : base(items) { Key = key; InternalList = items; } public KeyedList(IGrouping<TKey, TItem> grouping) : base(grouping) { Key = grouping.Key; InternalList = grouping; } }
(2)插入 CollectionViewSource
<Page.Resources> <CollectionViewSource x:Key="ItemsGrouped" IsSourceGrouped="True" ItemsPath="InternalList" Source="{Binding GroupData, Source={Binding}}"/> </Page.Resources>
这里的 GroupData 即为第一步中准备好的已分组的数据。
然后将 ListView 的 ItemsSource 绑定为 CollectionViewSources:
<ListView ItemsSource="{Binding Source={StaticResource ItemsGrouped}}"/>
(3)编写 ListView 的 GroupStyle.HeaderTemplate
GroupStyle.HeaderTemplate 也就是 ListView 里每一组的标题的模板,可自由定义:
<ListView.GroupStyle> <GroupStyle HidesIfEmpty="True" > <GroupStyle.HeaderTemplate> <DataTemplate> <Border Background="{StaticResource PhoneAccentBrush}" BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" Width="62" Height="62" Margin="0,0,18,0" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Key}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48" Padding="6" FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Center"/> </Border> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListView.GroupStyle>
(4)将 ListView 放入 SemanticZoom 里
到目前为止,ListView 已经能正常将分组数据显示在页面上了,最后一步也就是将 ListView 放入 SemanticZoom 控件中。
<SemanticZoom> <SemanticZoom.ZoomedInView> <!-- 前三步写好的 ListView --> </SemanticZoom.ZoomedInView> <SemanticZoom.ZoomedOutView> <!-- 缩放后的跳转界面 --> <GridView ItemsSource="{Binding Source={StaticResource ItemsGrouped}, Path=CollectionGroups}" Background="#AA000000"> <GridView.ItemTemplate> <DataTemplate> <Border Background="{Binding Converter={StaticResource BackgroundConverter}}" Padding="5"> <Border Background="{Binding}" Width="82" Height="82" HorizontalAlignment="Left"> <TextBlock Text="{Binding Group.Key}" Foreground="{Binding Converter={StaticResource ForegroundConverter}}" FontSize="48" Padding="6" HorizontalAlignment="Left" VerticalAlignment="Center"/> </Border> </Border> </DataTemplate> </GridView.ItemTemplate> </GridView> </SemanticZoom.ZoomedOutView> </SemanticZoom>
其中的两个 Converter 是对分组中有无该项的区分显示:
<Page x:Class="ListControls.GroupListViewPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ListControls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:prim="using:Windows.UI.Xaml.Controls.Primitives" mc:Ignorable="d" DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Page.Resources> <prim:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/> <prim:JumpListItemForegroundConverter x:Key="ForegroundConverter"/> </Page.Resources> </Page