Grouping GridView on Windows 8
It took me a few minutes to get the grouping working on the GridView on my Windows 8 app. I’m sharing what I did just so others can get it working sooner and go by the rest of their work.
In VS 2012, I added a Grouped Items Page to my Windows 8 application project.
By default, the template will add some sample data, so you can just run it to see how things look. Let’s see what it takes to show our custom data on the page. I’ll stat with the data source.
1: public class Team
2: {
3: public Team(string uniqueId, string teamName, string member, string manager,
4: int experienceInYears)
5: {
6: UniqueId = uniqueId;
7: TeamName = teamName;
8: Member = member;
9: Manager = manager;
10: ExperienceInYears = experienceInYears;
11: }
12:
13: public string UniqueId { get; set; }
14: public string TeamName { get; set; }
15: public string Member { get; set; }
16: public string Manager { get; set; }
17: public int ExperienceInYears { get; set; }
18: }
19:
20: public class TeamDataSource
21: {
22: public List<Team> Teams { get; set; }
23:
24: public TeamDataSource()
25: {
26: Teams = new List<Team>();
27:
28: Team team = new Team("devTeam", "Dev Team", "Arun", "Vijay", 9);
29: Teams.Add(team);
30: team = new Team("devTeam", "Dev Team", "Sanjeev", "Vijay", 10);
31: Teams.Add(team);
32: team = new Team("devTeam", "Dev Team", "Giri", "Sita", 3);
33: Teams.Add(team);
34: team = new Team("devTeam", "Dev Team", "Dinesh", "Vijay", 6);
35: Teams.Add(team);
36:
37: team = new Team("testTeam", "Testing Team", "John", "Rubin", 7);
38: Teams.Add(team);
39: team = new Team("testTeam", "Testing Team", "Vamsi", "Rubin", 5);
40: Teams.Add(team);
41: team = new Team("testTeam", "Testing Team", "Priyoj", "Babu", 2);
42: Teams.Add(team);
43: }
44: }
I'm going to use the CollectionViewSource in order to bind data to my grid view.
1: <CollectionViewSource
2: x:Name="groupedItemsViewSource"
3: Source="{Binding Groups}"
4: IsSourceGrouped="true"/>
My grid view has a very basic design. I have stripped it to the bare minimum, so u don’t see any margins, foreground styles or anything like that.
1: <GridView
2: x:Name="itemGridView"
3: ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}">
4: <GridView.ItemTemplate>
5: <DataTemplate>
6: <Grid HorizontalAlignment="Left" Width="140" Height="140">
7: <StackPanel Orientation="Vertical">
8: <TextBlock Text="{Binding Member}" />
9: <TextBlock>
10: <Run Text="Exp: "/>
11: <Run Text="{Binding ExperienceInYears}" />
12: <Run Text=" yrs"/>
13: </TextBlock>
14: <TextBlock>
15: <Run Text="Manager: "/>
16: <Run Text="{Binding Manager}" />
17: </TextBlock>
18: </StackPanel>
19: </Grid>
20: </DataTemplate>
21: </GridView.ItemTemplate>
22: <GridView.ItemsPanel>
23: <ItemsPanelTemplate>
24: <VirtualizingStackPanel Orientation="Horizontal"/>
25: </ItemsPanelTemplate>
26: </GridView.ItemsPanel>
27: <GridView.GroupStyle>
28: <GroupStyle>
29: <GroupStyle.HeaderTemplate>
30: <DataTemplate>
31: <Grid>
32: <TextBlock Text="{Binding Key}" />
33: </Grid>
34: </DataTemplate>
35: </GroupStyle.HeaderTemplate>
36: <GroupStyle.Panel>
37: <ItemsPanelTemplate>
38: <VariableSizedWrapGrid Orientation="Vertical" />
39: </ItemsPanelTemplate>
40: </GroupStyle.Panel>
41: </GroupStyle>
42: </GridView.GroupStyle>
43: </GridView>
The lines 31-33 is the grid that defines the header of the grid view and the lines 6-19 render the item itself. Line 32 says that we’ll be binding the ‘Key’ element as the header element. There is no ‘Key’ property in our data source classes. We’ll see that from the code below, where the binding actually happens.
1: protected override void LoadState(Object navigationParameter,
2: Dictionary<String, Object> pageState)
3: {
4: TeamDataSource teamlist = new TeamDataSource();
5: var teamGroups = teamlist.Teams.GroupBy(team=>team.TeamName)
6: .OrderBy(team => team.Key.ToString());
7:
8: DefaultViewModel["Groups"] = teamGroups;
9: }
The ‘Key’ is actually added by grouping. It basically means that the data is being ‘pivoted’ on some key, which in this case is the TeamMember property.
The benefit of doing it this ways is that we can group by some combination of the properties. We can always say, I’d like to group it by team name AND manager.
1: var teamGroups = teamlist.Teams.GroupBy(team => team.TeamName + "-" + team.Manager)
2: .OrderBy(team => team.Key.ToString());
You may have observed that our data source is pretty flat and it might not be the case all the time. Data usually comes in a hierarchical fashion. Let’s take that example as well.
1: public class Team
2: {
3: public Team(String member, String manager,
4: int experienceInYears)
5: {
6: Member = member;
7: Manager = manager;
8: ExperienceInYears = experienceInYears;
9: }
10:
11: public string Member { get; set; }
12: public string Manager { get; set; }
13: public int ExperienceInYears { get; set; }
14: }
15:
16:
17: public class TeamDetail
18: {
19: public string TeamName { get; set; }
20: public List<Team> Teams { get; set; }
21: }
22:
23: public class TeamDetailsSource
24: {
25: public List<TeamDetail> TeamDetails { get; set; }
26:
27: public TeamDetailsSource()
28: {
29: TeamDetails = new List<TeamDetail>();
30: TeamDetail teamDetail = new TeamDetail{ TeamName = "Devs team" };
31: TeamDetails.Add(teamDetail);
32:
33: teamDetail.Teams = new List<Team>();
34: Team team = new Team("Arun", "Vijay", 9);
35: teamDetail.Teams.Add(team);
36: team = new Team("Sanjeev", "Vijay", 10);
37: teamDetail.Teams.Add(team);
38: team = new Team("Giri", "Sita", 3);
39: teamDetail.Teams.Add(team);
40: team = new Team("Dinesh", "Vijay", 6);
41: teamDetail.Teams.Add(team);
42:
43: teamDetail = new TeamDetail { TeamName = "Test team" };
44: teamDetail.Teams = new List<Team>();
45: TeamDetails.Add(teamDetail);
46: team = new Team("John", "Rubin", 7);
47: teamDetail.Teams.Add(team);
48: team = new Team("Vamsi", "Rubin", 5);
49: teamDetail.Teams.Add(team);
50: team = new Team("Priyoj", "Babu", 2);
51: teamDetail.Teams.Add(team);
52: }
53: }
This is a typical hierarchical collection that I have.
The way I’ll be binding my data is by just initializing the collection.
1: var teamGroups = new TeamDetailsSource().TeamDetails;
2:
3: DefaultViewModel["Groups"] = teamGroups;
There is one other change that we’ll have to do to our CollectionViewSource. We need to provide the path for the collection where the items exist. This is done by setting the ItemsPath property. The ItemDetails class as a property called Teams which is what needs to be passed to the ItemsPath setting.
1: <CollectionViewSource
2: x:Name="groupedItemsViewSource"
3: Source="{Binding Groups}"
4: IsSourceGrouped="true"
5: ItemsPath="Teams"/>
IT JUST HAS TO WORK!