Reputation: 31
Windows Phone 8 SDK question using the LongListSelector
to group on dates.
I am familiar with the AlphaKeyGroup helper approach to grouping on letters.
Has anyone done/seen a similar write up for dates that is similarly locale aware? (Numbers would be a plus as well)
Upvotes: 3
Views: 2252
Reputation: 272
To use LongListSelector with numbers, lets try to group a list of people by age, instead of by the first letter of their first name (in the MSDN PeopleHub example)
They use the mysterious AlphaKeyGroup, which is a list of people with first names starting with the same letter (That letter becomes the Key of the AlphaKeyGroup). AlphaKeyGroups of people can look like this:
We're gonna use the IntegerKeyGroup, which is a list of people who have the same age. IntegerKeyGroups of people can look like this:
So to follow GentryRiggen's framework, we have to first define the IntegerKeyGroup, then stick people in the age groups they belong. I put these in ViewModel file.
public class IntegerKeyGroup<T> : List<T>
{
public int Key { get; private set; }
public IntegerKeyGroup(int key)
{
Key = key;
}
}
Notice IntegerKeyGroup is simply a List but with a special integer member called Key. This means we can label a list of people with an age integer as the Key.
Now we need to sort our big list of unsorted people into IntegerKeyGroups of different ages, and finally combine all these IntegerKeyGroups together. This combined list of IntegerKeyGroups is what LongListSelector accepts to be displayed.
public static List<IntegerKeyGroup<Person>> CreateGroups(IEnumerable<Person> UnsortedPeopleList)
{
// Create combined list of IntegerKeyGroups
List<IntegerKeyGroup<Person>> CombinedPeopleList = new List<IntegerKeyGroup<Person>>();
// Create a IntegerKeyGroup for each age group I want,
// The constructor parameters sets the Key to the IntegerKeyGroup
IntegerKeyGroup<Person> Age23s = new IntegerKeyGroup<Person>(23);
IntegerKeyGroup<Person> Age26s = new IntegerKeyGroup<Person>(26);
IntegerKeyGroup<Person> Age34s = new IntegerKeyGroup<Person>(34);
// Populate each IntegerKeyGroup with the appropriate Persons
foreach (Person p in UnsortedPeopleList)
{
switch (p.Age)
{
case 23: Age23s.Add(p); continue;
case 26: Age26s.Add(p); continue;
case 34: Age34s.Add(p); continue;
default: continue; // we don't support ages other than the 3 above
}
}
// Add each IntegerKeyGroup to the overall list
CombinedPeopleList.Add(Age23s);
CombinedPeopleList.Add(Age26s);
CombinedPeopleList.Add(Age34s);
return CombinedPeopleList;
}
Still in the ViewModel file, make the List of IntegerKeyGroups publicly accessible with the CreateGroups function.
public List<IntegerKeyGroup<Person>> AgeGroupedPeople
{
get
{
return CreateGroups(UnsortedPeople);
}
}
Now in the XAML, make 1 change to the original code from the MSDN PeopleHub example:
<phone:LongListSelector Name="peopleLongListSelector"
ItemsSource="{Binding AgeGroupedPeople}" <!-- Change is in this line! -->
JumpListStyle="{StaticResource LongListSelectorJumpListStyle}"
ListHeaderTemplate="{StaticResource LongListSelectorHeaderTemplate}"
GroupHeaderTemplate="{StaticResource LongListSelectorGroupHeaderTemmplate}"
ItemTemplate="{StaticResource LongListSelectorItemTemplate}"
HideEmptyGroups ="true" IsGroupingEnabled ="true" LayoutMode="List">
</phone:LongListSelector>
This should group people by integers, in this case, age.
Upvotes: 0
Reputation: 858
So I struggled with this one a bit too because the AlphaKeyGroup example from MSDN you mentioned is more complicated than it needs to be because of localization. What you are trying to do is create a new List object that that has one extra property, the Key. This Key property is the name that you group on. In the AlphaKeyGroup example it is each letter of the alphabet in your region. So create your own group object that inherits from List.
public class TimeKeyGroup<T> : List<T>
{
/// <summary>
/// The Key of this group.
/// </summary>
public string Key { get; private set; }
public TimeKeyGroup(string key)
{
Key = key;
}
}
Now create a method called CreateGroups that accepts an IEnumerable of the object you want to group and returns a list of you custom list object that you just created. In my implementation I was grouping Workout objects that had a TimeStamp property. In this method create group objects for each type of group key name you want such as "Last 7 Day" or "Last 6 Months". Then fill each group by loooping over the passed in IEnumerable group and evaluating each to determine where they should be grouped. Finally add each grouped list to a master group list and return it. Here is my method:
public static List<TimeKeyGroup<Workout>> CreateGroups(IEnumerable<Workout> workouts)
{
// Create List to hold each item
List<TimeKeyGroup<Workout>> groupedWorkouts = new List<TimeKeyGroup<Workout>>();
// Create a TimeKeyGroup for each group I want
TimeKeyGroup<Workout> LastSeven = new TimeKeyGroup<Workout>("Last Seven Days");
TimeKeyGroup<Workout> LastTwoWeeks = new TimeKeyGroup<Workout>("Last Two Weeks");
TimeKeyGroup<Workout> LastMonth = new TimeKeyGroup<Workout>("Last Month");
TimeKeyGroup<Workout> LastSixMonths = new TimeKeyGroup<Workout>("Last Six Months");
TimeKeyGroup<Workout> LastYear = new TimeKeyGroup<Workout>("Last Year");
TimeKeyGroup<Workout> AllTime = new TimeKeyGroup<Workout>("All Time");
// Fill each list with the appropriate workouts
foreach (Workout w in workouts)
{
if (w.TimeStamp > DateTime.Now.AddDays(-7))
{
LastSeven.Add(w);
continue;
}
else if (w.TimeStamp > DateTime.Now.AddDays(-14))
{
LastTwoWeeks.Add(w);
continue;
}
else if (w.TimeStamp > DateTime.Now.AddMonths(-1))
{
LastMonth.Add(w);
continue;
}
else if (w.TimeStamp > DateTime.Now.AddMonths(-6))
{
LastSixMonths.Add(w);
continue;
}
else if (w.TimeStamp > DateTime.Now.AddMonths(-12))
{
LastYear.Add(w);
continue;
}
else
{
AllTime.Add(w);
}
}
// Add each TimeKeyGroup to the overall list
groupedWorkouts.Add(LastSeven);
groupedWorkouts.Add(LastTwoWeeks);
groupedWorkouts.Add(LastMonth);
groupedWorkouts.Add(LastSixMonths);
groupedWorkouts.Add(LastYear);
groupedWorkouts.Add(AllTime);
return groupedWorkouts;
}
Now you have a nice list of grouped lists. Awesome! The rest is just hooking the itemssource property of your LongListSelector to this new list and defining a JumpListStyle and GroupedHeaderTemplate. The original article you referenced has all that info.
Good luck and happy Windows Phone Development!
Upvotes: 6
Reputation: 11
Well I use a modified version of the AlphaKeyGroup. This new class I called StringKeyGroup and creates the groups based on the first charachter of the items. So it's simply a matter of replacing AlphaKeyGroup with StringKeyGroup.
This new functionality can be used like:
myLonglistSelector.ItemSource = GroupedItems(myCollection);
....
public ObservableCollection<StringKeyGroup<myClass>> GroupedItems(IEnumerable<myClass> source)
{
return StringKeyGroup<myClass>.CreateGroups(source,
System.Threading.Thread.CurrentThread.CurrentUICulture,
s => s.Name, true);
}
Here's the code for StringKeyGroup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Globalization;
namespace MyNameSpace
{
public class StringKeyGroup<T> : ObservableCollection<T>
{
public delegate string GetKeyDelegate(T item);
public string Key { get; private set; }
public StringKeyGroup(string key)
{
Key = key;
}
public static ObservableCollection<StringKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
var list = new ObservableCollection<StringKeyGroup<T>>();
foreach (var item in items)
{
var itemKey = getKey(item).Substring(0, 1).ToLower();
var itemGroup = list.FirstOrDefault(li => li.Key == itemKey);
var itemGroupIndex = itemGroup != null ? list.IndexOf(itemGroup) : -1 ;
if (itemGroupIndex == -1)
{
list.Add(new StringKeyGroup<T>(itemKey));
itemGroupIndex = list.Count - 1;
}
if (itemGroupIndex >= 0 && itemGroupIndex < list.Count)
{
list[itemGroupIndex].Add(item);
}
}
if (sort)
{
foreach (var group in list)
{
group.ToList().Sort((c0, c1) => ci.CompareInfo.Compare(getKey(c0), getKey(c1)));
}
}
return list;
}
}
}
Upvotes: 1
Reputation: 1318
I've had success with this example from MSDN after I was stuck on the same example as you are now. The Group.cs file contains an implementation of a group which can be freely used with strings. My guess is, that you could easily add another property of DateTime and then you could try grouping by dates.
Upvotes: 2