Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Using the LongListSelector control on Windows Phone 7

DZone's Guide to

Using the LongListSelector control on Windows Phone 7

· Mobile Zone
Free Resource

Yesterday I looked into the Silverlight Toolkit for Windows Phone and I covered some of the controls that are included in the current build. One of the controls that might be a bit unusual to configure and get working is LongListSelector, that allows you to group items inside a list. With a regular ListBox, you can display data but there is no way to jump directly to an item set, and there is no way to group items.

To start, add a reference to the Microsoft.Phone.Controls.Toolkit library, that was bundled with the Silverlight Toolkit. If you are not sure where the library is located, check out my previous article to get the basic info about the distribution. Also, add a reference to the library by adding an additional XML namespace reference in the page tag:

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

Once done, you can create a control instance on the page itself, or in any other container that supports a control to be set as Content.

<toolkit:LongListSelector  x:Name="LongList">
</toolkit:LongListSelector>

But right now the list itself is nothing but a blank control. To fix this, first of all you need to define some templates that will show how exactly the data will be separated in the list, by using a list header, group headers, group item headers (used to switch between groups) and an item template.

In the page-based resource dictionary, add the following data templates:

<DataTemplate x:Key="GroupHeader">
<Border Background="{StaticResource PhoneAccentBrush}" Margin="{StaticResource PhoneTouchTargetOverhang}" Padding="{StaticResource PhoneTouchTargetOverhang}">
<TextBlock Text="{Binding Key}"/>
</Border>
</DataTemplate>

<DataTemplate x:Key="GroupItem">
<Border Background="{StaticResource PhoneAccentBrush}" Margin="{StaticResource PhoneTouchTargetOverhang}" Padding="{StaticResource PhoneTouchTargetOverhang}">
<TextBlock Text="{Binding Key}" Style="{StaticResource PhoneTextLargeStyle}"/>
</Border>
</DataTemplate>

<DataTemplate x:Key="ListHeader">
<TextBlock Text="Header" Style="{StaticResource PhoneTextTitle1Style}"/>
</DataTemplate>

<DataTemplate x:Key="ItemTmpl">
<Grid>
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
</DataTemplate>

These are templates very similar to those used in the sample provided on the Silverlight Toolkit website. I decided to use them here since those represent the best example of UI consistency with the existing Windows Phone elements. But it's not the appearance that matters here.

The first template is GroupHeader - this is used to separate content and is used to represent a set of items grouped by a common property. If you look at the binding, it is bound to the Key property, that is dependent on ItemsSource set to the main list.

GroupItem is used to represent the same group header, but in the context of a jump list that appears when user touches an actual group header (that is how it is possible to jump to different item categories). It is bound to the same Key property as each of the group headers.

ListHeader is displayed at the top of the list when it is loaded. It is not moved with the list as that is scrolled and it is also not associated with any group, therefore it won't be displayed above a group header other than the first one. I left this one un-bound, but you can certainly bind it to a super-category name (passed somewhere in a list or a separate property).

ItemTmpl represents the appearance of each item that falls into a specific category. In my case, I am only using a TextBlock control, however you can add a much more rich set (e.g. include images and/or multiple controls). Currently, it is bound to the Title property, which is a part of a custom object I am passing, but I will get there later.

Now I need to actually bind the list to a collection of items that are differentiated in categories. And here, an interesting process is involved. First of all, I am going to create a custom class that will represent an Item object:

public class Item
{
public string Title { get; set; }
public string Content { get; set; }
}

It is pretty simple - two string properties that can be set and retrieved. The Content property in my case will be the grouping key. But there is no actual list yet that can be bound.

So, when my main page is initializing, I am also creating a list of Item and populating it with sample content:

List<Item> mainItem = new List<Item>();

for (int i = 0; i < 20; i++)
{
mainItem.Add(new Item() { Content = "Category A", Title = "Sample " + i.ToString() });
mainItem.Add(new Item() { Content = "Category B", Title = "Sample B" + i.ToString() });
mainItem.Add(new Item() { Content = "Category C", Title = "Sample C" + i.ToString() });
}

But here is another problem - the items aren't grouped. Indeed, these are divided in elements that have different Content property values (and there are 20 items that have this property set to the same value), but as such - there are no groups.

Right now is the case to use the IGrouping interface, and I am get an instance of it for each object set via LINQ:

var selected = from c in mainItem group c by c.Content into n select new GroupingLayer<string,Item>(n);

But look what's here - instead of using IGrouping with the same generic parameters, I am using GroupingLayer. It is an implementation of the IGrouping interface that exposes the Key property, used to set group names. If I would pass an IGrouping instance, I wouldn't be able to bind to the Key property since it is internal. GroupingLayer is an abstraction layer on top of that:

public class GroupingLayer<TKey, TElement> : IGrouping<TKey, TElement>
{

private readonly IGrouping<TKey, TElement> grouping;

public GroupingLayer(IGrouping<TKey, TElement> unit)
{
grouping = unit;
}

public TKey Key
{
get { return grouping.Key; }
}

public IEnumerator<TElement> GetEnumerator()
{
return grouping.GetEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return grouping.GetEnumerator();
}
}

So once the LINQ query executes, you get an instance of IEnumerable<GroupingLayer<string,Item>>. The group header will automatically link to the string value and every item grouped will be linked to the Item instance stored in the object.

You are now able to bind the list to the control:

LongList.ItemsSource = selected;

And of course, the items will be displayed in the list:

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}