4. ListPicker Control
I know developers love ListBoxes
. For the phone, sometimes the ListBox
is just the wrong tool. For very short lists of options, ListBox
es take up too much screen real estate. As an alternative, the toolkit gives you the ListPicker
. The ListPicker
is closer to the ComboBox
control that is commonly used on websites and desktop applications.
The ListPicker
control is a good solution when you have a short list from which the user must select one item. In fact, the ListPicker
could also be used to replace radio buttons. The ListPicker
shows the currently selected item in a box much like a TextBox
, as shown in Figure 6.
FIGURE 6 ListPicker example (closed)
When the user touches the ListPicker
,
it opens in one of two ways. If the list is short (five items or less),
it expands the control to show the options, as shown in Figure 7.
FIGURE 7 ListPicker example (opened)
If the list has more than five options, it pops up a full-screen list of options from which to choose, as shown in Figure 8.
FIGURE 8 ListPicker example (full screen)
To create a ListPicker
, you can create it as a simple XAML element, like so:
<TextBlock Style="{StaticResource PhoneTextLargeStyle}"
Text="Pick a Color" />
<toolkit:ListPicker x:Name="thePicker" />
<Button Content="This is a Button" />
Because the ListPicker
is a list control, you can use the ItemsSource
to specify the list:
...
thePicker.ItemsSource = new string[]
{
"Blue",
"Green",
"Red",
"Orange",
"Purple",
"Cyan",
"Brown",
"Gray",
"Light Green"
};
...
5. LongListSelector Control
As an alternative to using the ListBox
for very long lists, the Toolkit also supplies you with the LongListSelector
control4
that lets users look at large numbers of options. The phone uses this
when you select a phone number from your address book. Because the list
of people could be quite long, it categorizes the people by the first
letter of their first or last name. Although it supports three modes,
the real gem is the capability to have a pop-up list of groups to help
users locate the items they’re looking for. Although this type of
control is used for the address book, that application uses the first
letter for grouping; it’s completely up to you how you decide to group
the objects in the LongListSelector
. For example, Figure 9 shows a list of games grouped by genre.
FIGURE 9 LongListSelector with groups
This shows the groups to
the user and lets him tap on the group to pop up an overlay of groups to
help him navigate large lists effectively, as shown in Figure 10.
FIGURE 10 LongListSelector’s pop-up groups
To use this control, you have to lean on data binding to set up three elements of your control:
• ItemTemplate: This is the contents of the individual data for an item in the LongListSelector
(a “Game” in the preceding example).
• GroupHeaderTemplate: This is the item above the list of grouped objects (the “Genre” in the preceding example) shown in the main UI of the LongListSelector
.
• GroupItemTemplate: This is the display for a group in the pop-up.
It is common to use the same template for the group item and the group header. Here is an example of the XAML to create a LongListSelector
:
<Grid x:Name="LayoutRoot"
Background="Transparent">
<Grid.Resources>
<DataTemplate x:Key="letterTemplate">
<Border Background="{StaticResource PhoneAccentBrush}"
Margin="4">
<TextBlock Text="{Binding GroupName}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Style="{StaticResource PhoneTextGroupHeaderStyle}" />
</Border>
</DataTemplate>
</Grid.Resources>
<phone:LongListSelector x:Name="theSelector"
GroupHeaderTemplate="{StaticResource letterTemplate}"
GroupItemTemplate="{StaticResource letterTemplate}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="75"
Source="{Binding ImageUrl}" />
<TextBlock Text="{Binding Name}"
Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
Notice first that the XAML stores a DataTemplate
for the group in a resource. It does this so that we can use the same template for both the GroupHeaderTemplate
and the GroupItemTemplate
. Next, the ItemTemplate
is specified inline.
When you apply data to the control, you can assign the ItemsSource
like any other list control. The problem is that to make the control
work, it expects your data to be in a specific format. This format is a
collection of groups. A group is just a collection that often has something that describes it (such as the name of the genre in our example). The Windows Phone SDK already has something like this called the IGrouping<T,T>
interface. You might try to just use the grouping semantics in LINQ to accomplish this, like so:
// THIS DOES NOT WORK
// Use LINQ to Group
var games = new GameList();
var qry = from g in games
orderby g.Genre, g.Name
group g by g.Genre into genres
select genres;
// Bind the collection of Groups into the control
var result = qry.ToList();
theSelector.ItemsSource = result;
This LINQ query sorts the games by the name of
the genre and the name of the game and then groups that result into
collections of names by genre. This sounds very much like what we need
for the control. Unfortunately, the underlying class that handles the
grouping does not support data binding because the name of the group is
not public.5 To solve this, you can use a simple wrapper for the grouping, like so:
public class Group<T> : List<T>
{
public Group(IGrouping<string, T> group)
{
GroupName = group.Key;
this.AddRange(group);
}
public string GroupName { get; set; }
}
This class adds a public property for the name of the group and constructs itself from an IGrouping<T,T>
object that LINQ uses (although it assumes a string-based key). Remember, not only does this group have a GroupName
property to identify the group to the user, but also is a collection of the underlying objects. This is the data format this control requires, which is why we needed this class. This way, you can modify the LINQ query to construct these instead of returning the raw IGrouping<T,T>
interface:
// Use LINQ to Group
var qry = from g in games
orderby g.Genre, g.Name
group g by g.Genre into genres
select new Group<Game>(genres);
This works because our grouping data template uses the GroupName
we specified in the Group<T>
class to do the data binding:
<DataTemplate x:Key="letterTemplate">
<Border Background="{StaticResource PhoneAccentBrush}"
Margin="4">
<TextBlock Text="{Binding GroupName}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Style="{StaticResource PhoneTextGroupHeaderStyle}" />
</Border>
</DataTemplate>
The LongListSelector
supports other
templates and properties to control the way you present this to the
user, but understanding the basics of how to get a simple version of the
control working will help you get started with the control.