Modifying Windows Store Grid App – IEnumerable, Data Binding, LINQ Queries

Yesterday I was trolling to answer questions on MSDN forums for Windows 8.

I came across an interesting question. The person asking the question wanted to know how to modify the the starting content for a Windows Store Grid App style application.

You can create an excellent starting point for an application using this template.
kwiq15nu
After creating a new project from this template, you can start to browse the code and noticed that the data model is all baked in. Your project will contain a data model folder. Inside that folder you will find the file SampleDataSource.cs. Here is a partial view of the data model being built.

1234567891011121314151617181920 var group1 = new SampleDataGroup("Group-1",        "Group Title: 1",        "Group Subtitle: 1",        "Assets/DarkGray.png",        "Group Description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus tempor scelerisque lorem in vehicula. Aliquam tincidunt, lacus ut sagittis tristique, turpis massa volutpat augue, eu rutrum ligula ante a ante");group1.Items.Add(new SampleDataItem("Group-1-Item-1",        "Item Title: 1",        "Item Subtitle: 1",        "Assets/LightGray.png",        "Item Description: Pellentesque porta, mauris quis interdum vehicula, urna sapien ultrices velit, nec venenatis dui odio in augue. Cras posuere, enim a cursus convallis, neque turpis malesuada erat, ut adipiscing neque tortor ac erat.",        ITEM_CONTENT,        group1));group1.Items.Add(new SampleDataItem("Group-1-Item-2",        "Item Title: 2",        "Item Subtitle: 2",        "Assets/DarkGray.png",        "Item Description: Pellentesque porta, mauris quis interdum vehicula, urna sapien ultrices velit, nec venenatis dui odio in augue. Cras posuere, enim a cursus convallis, neque turpis malesuada erat, ut adipiscing neque tortor ac erat.",        ITEM_CONTENT,        group1));

As you can see from the data above a simple parent-child relationship exists between groups and items. The data model is composed of many groups. Each group contains several items. And every item can point back to the group that it belongs to.

This is just a simple tree of data. It is a hierarchical collection of data.

Here is what the default starting page looks like. This is the default look and feel unchanged by me.

z1roe0tu
But imagine that you wanted it to look like this. In other words, you want a combo box at the top that contains all the groups. By selecting one of the groups in the combo box you narrow your view to just that group to be viewed.

xgg3qhma
Notice in the image above that the group selected in the combo box is the only group and items visible below it. Group Title 4. The combo box narrows down the amount of groups that can be seen to just one.

We will need to learn several concepts:

How to data bind combo boxes
How to issue Linq queries against the collections just discussed.
Have to retrieve multiple fields from a combo box selection
How to work with some of the more advanced C sharp constructs (IEnumerable<T>, etc)

The GroupedItemsPage.xaml represents the user interface for the running application you see above. Since this is a XAML/C# project, I will need to go into the code behind as well (GroupedItemsPage.xaml.cs).

Let’s talk about the xaml first. Here is my <Page.Resources> section.

     <Page.Resources>

        <!--
            Collection of grouped items displayed by this page, bound to a subset
            of the complete item list because items in groups cannot be virtualized
        -->
        <CollectionViewSource
            x:Name="groupedItemsViewSource"
            Source="{Binding Groups}"
            IsSourceGrouped="true"
            ItemsPath="TopItems"
            d:Source="{Binding AllGroups, 
            Source={d:DesignInstance Type=data:SampleDataSource, 
            IsDesignTimeCreatable=True}}"/>
        <!--I ADDED THIS-->
      < CollectionViewSource            x:Name
 ="groupTitles"<br>            /> 
    </Page.Resources>


I added a new collection view source called groupTitles. My code behind will update this collection.

Our next task is to add the markup for the combo box you saw at the top.

 <!-- Back button and page title -->
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
         < ColumnDefinition Width ="Auto"/> 
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
    <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Grid.Column="1" IsHitTestVisible="false" Style="{StaticResource PageHeaderTextStyle}"/>
    <!--I ADDED THIS-->
 <ComboBox Name="groupComboBox" Grid.Column ="2"            ItemsSource="{Binding Source={StaticResource groupTitles
 }}"                Height="60" SelectionChanged
 ="Group_SelectionChanged"><br>        <ComboBox.ItemTemplate >             
 <!-- Data is TimeZoneDisplayInfo -->            <DataTemplate
 ><br>                <TextBlock Text="{Binding Title }" /><br>            </DataTemplate ><br>        </ComboBox.ItemTemplate ><br>    </ComboBox > 
</Grid>

There are a couple things to notice in the combo box above. Note that the binding source is groupsTitles. Note that the textblock binds to Title.

Here is the key learning point . This means that whatever collection I add to groupTitles, the better have a column called Title.

Let’s now turn our attention to the code behind. The code behind will not interact with the combo box. It will interact with collection view source, whose name is groupTitles.

One of the first things I wanted to do was declare a class that I would bind to the combo box. It would need to columns. The first column would be the group name itself to display to the user. The second column I needed was the group ID, which was used to do a lookup and narrow the viewable groups using that group ID. group ID is UniqueId in the system. But those fields are included in the GroupName class as seen below:

The combo box will be bound to a collection of GroupName objects

GroupName Class
12345678910 public class GroupName : IComparable<GroupName> { public string Title { get; set; } public string UniqueId { get; set; } int IComparable<GroupName>.CompareTo(GroupName other) { return String.Compare(this.UniqueId, other.UniqueId); } }

Notice that the class above has a CompareTo() method that is necessary if you ever plan to sort collections of GroupName objects. I am arbitrarily sorting by the group ID.

Building the collection to populate the combo box

The code below comes from the GroupedItemsPage.xaml.cs file. The LoadState() method comes for free. I just added a new IEnumerableCollection.

LoadState()
123456789101112protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState){ // TODO: Create an appropriate data model for your problem domain to replace the sample data IEnumerable<SampleDataGroup> sampleDataGroups = SampleDataSource.GetGroups((String)navigationParameter); this.DefaultViewModel["Groups"] = sampleDataGroups; // HERE IS WHAT I ADDED Manually grab a unique list of Groups IEnumerable<GroupName> groupname = sampleDataGroups.Select(i => new GroupName { Title = i.Title, UniqueId = i.UniqueId }).Distinct().OrderByDescending(s => s); groupTitles.Source = groupname;}

Notice that in the code above I issue a query to retrieve a distinct list of group names. sampleDataGroups was given to us for free when we said File New Project. Visual studio conveniently built up all the data into that single collection object. We can just query off of it and get the data we need.

Remember from before we declared the CollectionViewSource :

         <CollectionViewSource
            x:Name="groupTitles"
            />

So the last step is to simply attach the groupname collection to groupTitles.Source.

How to narrow down the viewable list of groups

After user selects one of the groups from the combo box above, our software will need to narrow the number of visible groups to the user. The combo box will raise a selection changed event any time the user chooses one of the groups from it. Once this event fires off it will be easy to extract the group ID from the selection. Once we have the group ID we will issue another query against the main data source to narrow the visible groups to just the one that was selected by the user.

Remember that we did define the event for a changed selection.

     <ComboBox Name="groupComboBox" Grid.Column="2"
                    ItemsSource="{Binding Source={StaticResource groupTitles}}"
                     Height="60" SelectionChanged ="Group_SelectionChanged"> 

Every time a user changes the group selected in the combo box, the event below will fire off.

It starts by extracting the group ID corresponding to the group selected by the user. It then passes that ID to the GetGroup2() method, which is provided below.

123456789private void Group_SelectionChanged(object sender, SelectionChangedEventArgs e){    // Grab group name and filter rest of groups visible.    GroupName gn = (GroupName)this.groupComboBox.SelectedItem;    IEnumerable<SampleDataGroup> sampleDataGroups = SampleDataSource.GetGroup2(gn.UniqueId);    this.DefaultViewModel["Groups"] = sampleDataGroups;}

Note the last line in the code above. Whatever you assigned to the default view model “Groups” is what appears as visible groups. Basically you control what is visible by making your assignment there. So whatever records your LINQ queries return, will be the records visible by the user on the starting screen.

The GetGroup2() method can be found in the SampleDataSource.cs file (public sealed class SampleDataSource).

123456789// I added this codepublic static IEnumerable<SampleDataGroup> GetGroup2(string uniqueId){    // Simple linear search is acceptable for small data sets    var matches = _sampleDataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));    if (matches.Count() == 1) return matches;    return null;}

The GridView code that is given to you by the system is bound to the collection above _sampleDataSource.

Hopefully this post give you some pointers on how to work with the built-in data sources. From here you can learn more about LINQ queries and how they can be used to control the amount of viewable records bound to controls. What this post tried to accomplish was simple:

Talk at a high level about how data gets represented in the Windows store grid app application
Discuss how Linq queries can be executed against existing data sources to control the records that are visible to the user.
Illustrate how to bind a combo box to a collection of objects