Windows Phone 8 XAML LongListSelector

This blog post was authored by Rohan Thakkar, a Program Manager on the Windows Phone team.

- Adam


Last year we evangelized the use of the LongListSelector control to provide smooth infinite scrolling scenarios. The LongListSelector control is so useful that we decided to enhance it and move it to the SDK for Windows Phone 8. In this post we discuss the improvements we’ve made to the control, and provide a mapping guide for Windows Phone developers who are already using the Windows Phone Toolkit 7.1 version of the LongListSelector.

If you prefer to read XAML instead of plain text, you can go straight to the samples and explore them:

For more info about how to use the LongListSelector, see How to display data in a grouped list in LongListSelector for Windows Phone 8.

Let’s dig a little deeper into various enhancements we’ve made to the control. The following sections are provided in this post.

  1. LongListSelector moved to Windows Phone 8 SDK and to ROM
  2. Sticky headers
  3. Grouped grid layout
  4. Improvements to infinite scrolling scenario
  5. Globalization – SortedLocaleGrouping
  6. LongListSelector mapping guide – Differences between Windows Phone OS 7.1 Toolkit and Windows Phone 8.0 SDK
  7. FAQs

LongListSelector moved to Windows Phone 8 SDK and to ROM

The LongListSelector control is now a part of Microsoft.Phone.Controls namespace in Microsoft.Phone.Controls.dllassembly. This means it’s now a fully supported, high-quality control shipped by the Windows Phone Development team. We also moved the assembly to ROM to take advantage of the internal off-thread input and the render thread architecture. This means the control is optimized for the full potential of Windows Phone.

The Microsoft.Phone.Controls.dll assembly also contains other phone-specific controls, such as Panorama and Pivot. These controls also get the performance benefits of being in ROM, including reduced memory consumption (~50% reduced memory for a basic app) and improved touch performance, especially when you have data being loaded in the panorama view.

Sticky headers

The native Windows Phone grouped list has the headers stick to the top as you scroll. The LongListSelector control in Windows Phone 8 has the same smooth effect.

Note the headers in the following screenshots.

clip_image002

Figure 1 – Notice group header “a”

 

clip_image004

Figure 2 – Scrolling up, notice how “a” sticks to top

 

clip_image006

Figure 3 – Notice how the group header “b” pushes “a”

 

clip_image008

Figure 4 – Now “b” is sticky, same as “a”

 

Grouped grid layout

In Windows Phone 8, LongListSelector supports grid layout, which is more than just the WrapPanel that’s available in the Windows Phone Toolkit. The LongListSelector grid layout is virtualized, which provides better performance. The following screenshots are from the PhotoHubsample.

clip_image010

Figure 5 – LongListSelector’s Grid layout

 

clip_image012

Figure 6 – Jumplist’s List layout.

 

The following code is an excerpt of the XAML for the LongListSelector from the PhotoHub sample.

C#
  1. <phone:PhoneApplicationPage.Resources>
  2. <phone: JumpListItemBackgroundConverter x:Key=“BackgroundConverter”/>
  3.         <phone:JumpListItemForegroundConverter x:Key=“ForegroundConverter”/>
  4.  
  5.         <Style x:Key=“JumpListStyle” TargetType=“phone:LongListSelector”>
  6.             <Setter Property=“LayoutMode” Value=“List” />
  7.             <Setter Property=“Margin” Value=“12,12,0,0”/>
  8.             <Setter Property=“ItemTemplate”>
  9.                 <Setter.Value>
  10.                     <DataTemplate>
  11.                         <Border Background=“{Binding Converter={StaticResource BackgroundConverter}}”
  12.                             Width=“470”
  13.                             Height=“70”
  14.                             Margin=“6”>
  15.                             <TextBlock Text=“{Binding Key}”
  16.                                 Foreground=“{Binding Converter= {StaticResource ForegroundConverter}}”
  17.                                 Font Family=“{StaticResource PhoneFontFamilySemiBold}”
  18.                                   FontSize=“28”
  19.                                 Padding=“2”
  20.                                 VerticalAlignment=“Bottom”/>
  21.                         </Border>
  22.                     </DataTemplate>
  23.                 </Setter.Value>
  24.             </Setter>
  25.         </Style>
  26. </phone:PhoneApplicationPage.Resources>
  27. <phone:LongListSelector Name=“PhotoHubLLS” Margin=“13,-30,0,0″
  28.     ItemsSource=“{Binding GroupedPhotos}”
  29.                          ItemTemplate=“{StaticResource ItemTemplate}”
  30.     GroupHeaderTemplate=“{StaticResource GroupHeader}”
  31.     JumpListStyle=“{StaticResource JumpListStyle}”
  32.     IsGroupingEnabled=“True”
  33.     LayoutMode=“Grid”
  34.     GridCellSize=“108,108”/>

Let’s look at some examples of interesting properties found in this code.

LayoutModeIndicates whether the collection in the context should be displayed as a list or as a grid. In the preceding XAML example, LayoutMode is set to Grid as a property on the LongListSelector, and set to List as a property on JumpListStyle.

JumpListStyle– Provides the style for the jump list items.

GridCellSize – Indicates the size of each cell in the grid. This property has to be set on the LongListSelector or the JumpListStylewherever the grid layout is to be rendered.

Converters – Note that the JumpListItemBackgroundConverter and JumpListItemForegroundConverterare needed to convert the foreground and background colors of the jump list item (group header of a group) whether or not it has items in the group. These converters also are part of the SDK and are in the same Microsoft.Phone.Controls namespace.

clip_image013

Figure 7 – Value converters shading empty group jump list items

 

The default foreground and background colors match the first-party app. You can customize it to align with your design needs using the Enabled and Disabled properties as shown here:

XAML
  1. <phone:JumpListItemBackgroundConverter Disabled=”Bisque” Enabled=”Aqua” x:Key=”BackgroundConverter”/>
  2. <phone:JumpListItemForegroundConverter Disabled=”Azure” Enabled=”BlueViolet” x:Key=”ForegroundConverter”/>

Improvements to infinite scrolling scenarios

Earlier we spoke about infinite scrolling for the Windows Phone Toolkit 7.1 LongListSelector. As you approach the end of the visible list when scrolling, the LongListSelector automatically fetches more items and adds them to the list, which gives you a sense of infinite scrolling. We made this scenario simple to implement in Windows Phone 8. The following code from the Twitter Search sampledemonstrates how easy it is to implement:

XAML
  1. <phone:LongListSelector Name=”resultList” Grid.Row=”1″
  2.     DataContext=”{StaticResource viewModel}
  3.     ItemTemplate=”{StaticResource ResultItemTemplate}
  4.     ItemsSource=”{Binding TwitterCollection}
  5.     ListFooter=”{Binding}
  6.     ItemRealized=”resultList_ItemRealized”/>

 

C#
  1. void resultList_ItemRealized(object sender, ItemRealizationEventArgs e)
  2. {
  3.     if (!_viewModel.IsLoading && resultList.ItemsSource != null && resultList.ItemsSource.Count >= _offsetKnob)
  4.     {
  5.         if (e.ItemKind == LongListSelectorItemKind.Item)
  6.         {
  7.             if ((e.Container.Content as TwitterSearchResult).Equals(resultList.ItemsSource[resultList.ItemsSource.Count – _offsetKnob]))
  8.             {
  9.                 _viewModel.LoadPage(_searchTerm, _pageNumber++);
  10.             }
  11.         }
  12.     }
  13. }

The ItemRealized event is raised every time a LongListSelector item acquires a UI container to be displayed on the screen. In other words, every time an item enters the UI buffers above or below the current viewport, the ItemRealized event is raised. The event argument property ItemKind indicates whether the UI container is an Item, ListHeader, GroupHeader, or ListFooter. Using the property Container.Contentyou can get the actual object associated with the UI container that was realized. This way you can monitor the objects within the UI container buffer.

Note how the app code in this example contains a private variable _offsetKnob. This helps fine-tune the LongListSelector scrolling experience by helping to determine when to load more items depending on how heavy your item template is, or on how slow the response is from the service sending the data.

Globalization – SortedLocaleGrouping

When you group contacts in alphabetical order, you want to do this in all languages. You could provide your own globalized, sorted alphabet characters for group headers. However, using your own alphabet strings will not guarantee 100% matching with the phone first-party contacts list. So we introduced a class called SortedLocaleGrouping that provides a set of alphabets that are the same as those used by the first party. In the PeopleHub sample, SortedLocaleGrouping (from Microsoft.Phone.Globalization namespace) is used in one of the helper types, AlphaKeyGroup(which also is very highly recommended).

You get the group display names by using the SortedLocaleGrouping.GetGroupDisplayNames property to populate your groups. Then use the GetGroupIndexmethod to classify your contacts list.

SupportsPhonetics property

This is a unique property and we have included it in SortedLocaleGrouping. In some cultures, such as ja-JP, last names are written in a different script and are pronounced differently depending on the region or background.

Here’s an example:

String of LastName

Yomi/Pronunciation

Group based on Yomi

Group without Yomi

新井 (display string)

Nii –> にいい (in Hiragana) or ニイイ (in Katakana)

The Globe icon

 

Arai –> あらい (in Hiragana) or アライ (in Katakana)

The Globe icon

 

Shinkyo –> しんきょ (in Hiragana) or シンキョ (in Katakana)

The Globe icon

This is a common phenomenon catching on in different cultures, and the first-party contacts list will be grouped based on the Yomi that is passed in. If no Yomi is passed in, it will return the default, same group (equivalent to other or globe).

You usually would use the SupportsPhonetics property when classifying the contacts into groups and if you have the Yomi (pronunciation) database that you can access via a service or other means for the specific culture. The code between the //EXAMPLE comments in the following example is from one of the helper methods that shows how you would use it if you have a database of the pronunciations (Yomi) for various names.

C#
  1. /// <summary>
  2. /// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
  3. /// </summary>
  4. /// <param name=”items”>The items to place in the groups.</param>
  5. /// <param name=”ci”>The CultureInfo to group and sort by.</param>
  6. /// <param name=”getKey”>A delegate to get the key from an item.</param>
  7. /// <param name=”sort”>Will sort the data if true.</param>
  8. /// <returns>An items source for a LongListSelector</returns>
  9. public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, Func<T, string> keySelector, bool sort)
  10. {
  11.     SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
  12.     List<AlphaKeyGroup<T>> list = CreateDefaultGroups(slg);
  13.  
  14.     foreach (T item in items)
  15.     {
  16.         int index = 0;
  17.         // EXAMPLE
  18.         if (slg.SupportsPhonetics)
  19.         {
  20.             //check if your database has Yomi string for item
  21.             //if it does not, then do you want to generate Yomi or ask the user for this item.
  22.             index = slg.GetGroupIndex(Yomiof(item));
  23.         }
  24.         // EXAMPLE
  25.         else
  26.         {
  27.             index = slg.GetGroupIndex(keySelector(item));
  28.         }
  29.  
  30.         if (index >= 0 && index < list.Count)
  31.         {
  32.             list[index].Add(item);
  33.         }
  34.     }
  35.  
  36.     if (sort)
  37.     {
  38.         foreach (AlphaKeyGroup<T> group in list)
  39.         {
  40.             group.Sort((c0, c1) => { return ci.CompareInfo.Compare(keySelector(c0), keySelector(c1)); });
  41.         }
  42.     }
  43.  
  44.     return list;
  45. }

LongListSelector mapping guide – Differences between Windows Phone Toolkit 7.1 and Windows Phone 8.0 SDK

We modified and enhanced the Windows Phone Toolkit LongListSelector. In this section we briefly outline what has changed.

Properties modified from the Windows Phone Toolkit 7.1

Deleted

  • BufferSize
  • IsBouncy
  • IsScrolling
  • MaximumFlickVelocity
  • ShowListFooter/ShowListHeader

Modified

Windows Phone Toolkit 7.1

Windows Phone 8 ROM SDK

DisplayAllGroups

Display all groups in the list whether or not they have items. Default is true.

HideEmptyGroups

Hide all groups in the list without items. Default is false.

GroupItemTemplate

JumpListStyle

IsFlatList

Gets or sets whether the list is flat instead of a group hierarchy. Default is true.

IsGroupingEnabled

Gets or sets whether the list is flat instead of a group hierarchy. Default is false.

New concepts

  • GridCellSize
  • LayoutMode LongListSelectorLayoutMode { List, Grid };
  • ManipulationState
    C#
    1. public enum ManipulationState
    2. {
    3.     Idle, // nothing is manipulating or animating
    4.     Manipulating, // Gesture is being recognized, finger is down and any delta is received, drag/pan or flick
    5.     Animating //No Gesture is currently happening, but there is some animation happening, like scroll animation or compression animation
    6. }

Methods modified from the Windows Phone Toolkit 7.1

Deleted

  • AnimateTo(object item)
  • CloseGroupView()
  • DisplayGroupView()
  • GetItemsInView()
  • GetItemsWithContainers(bool onlyItemsInView, bool getContainers)
  • ScrollToGroup(object group)

Events modified from the Windows Phone Toolkit 7.1

Deleted

  • StretchingBottom
  • StretchingCompleted
  • StretchingTop

Modified

Windows Phone Toolkit 7.1

Windows Phone 8 ROM SDK

ScrollingCompleted

ScrollingStarted

ManipulationStateChanged

(coupled with ManipulationState property)

Link/Unlink

ItemRealized/ ItemUnrealized

With EventArgs including ItemKind

C#
  1. public class ItemRealizationEventArgs : EventArgs
  2. {
  3.     /// <summary>
  4.     /// The ContentPresenter which is displaying the item.
  5.     /// </summary>
  6.     public ContentPresenter Container { get; }
  7.  
  8.     /// <summary>
  9.     /// Gets the kind of item that is realized
  10.     /// </summary>
  11.     public LongListSelectorItemKind ItemKind { get; }
  12.  
  13. }
  14. /// <summary>
  15. /// Different kinds of items that exists in LongListSelector
  16. /// </summary>
  17. enum LongListSelectorItemKind
  18. {
  19.     ListHeader,
  20.     GroupHeader,
  21.     Item,
  22.     GroupFooter,
  23.     ListFooter
  24. }

FAQ

Why use IList instead of IEnumerable for ItemsSource?

You have to know how many elements there are and there is no Count property on IEnumerable. You also have to be able to access the items by index, not just in the forward-only, one-at-a-time way that enumerators do. If ItemsSource accepted IEnumerables or an IEnumerable of IEnumerables, you’d have to convert them to lists internally using the default method of iterating over every single item. This conversion would rule out data virtualization, which could cause poor performance without the app author knowing why. Worse yet, the author might think that it is the status quo and simply accept a longer start time. This was one of the complaints about the original toolkit LongListSelector. Now, if an app author has only an IEnumerable set of data, the developer can simply call ToList() for the default conversion to an IList but the developer also has the flexibility provide their own IListimplementation.

Is LongListSelector recommended instead of ListBox?

Yes! We have designed the LongListSelector specifically for phone scenarios and we encourage people to use the LongListSelector instead of ListBox for phone apps.