WP7: Everything About ListBox Performance

Standard

If you found this blog post, I bet you’ve already had a hard time dealing with the unsatisfactory performance of the Silverlight framework of WP7. Let’s face it, managed code has no chance against native code (read: objective-c) when coming to execution performance. And one of the most frustrating part of WP7’s performance issue could be scrolling a ListBox-like control.

Boldly speaking, ListBox-like control is the most critical control for a WP7 Silverlight app. For any kind of average complex WP7 app, you’ll have to use a scrollable list in one or more of your screens.

Stack Panel

The golden rule here is that the simpler the UI is, the faster the performance can be. So if you list is small and static enough, like less than 30 items not using an ObservableCollection, you may try putting a StackPanel in a ScrollViewer and have the items in the StackPanel generated in code.

Items Control

If what you need is just a list that binds to a collection and executes some code or navigates to another screen when you pressed on one of its items, you could consider re-implementing your ListBox using the ItemsControl.

The standard ListBox extends the Selector class which further extends ItemsControl.

ListBox Inheriting Structure

The ItemsControl class has all of the important ListBox properties such as ItemsSource and ItemTemplate. What it is lacking is the ability to “select” an item and it doesn’t scroll by itself. Therefore, you may implement your list in the following way:

<ScrollViewer VerticalScrollBarVisibility="Visible">
    <ItemsControl ItemsSource="{Binding MyDataCollection}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <HyperlinkButton NavigateUri="/TargetPage.xaml" Click="DoSomeThing">
                    <HyperlinkButton.Template>
                        <ControlTemplate x:Key="SimpleHyperlinkButton" TargetType="HyperlinkButton">
                            <ContentPresenter Content="{TemplateBinding Content}"/>
                        </ControlTemplate>
                    </HyperlinkButton.Template>
                    <!-- Your control template here -->
                </HyperlinkButton>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

As you can see, you can bind collection to the ItemsControl‘s ItemsSource property just like a ListBox. The use of the HyperlinkButton lets you directly set the URI of the new page that you want to navigate to, saves from writing event handler in the code behind. Or you can also use the Click event to do extra processing in code. Note that overwriting the HyperlinkButton‘s Template is compulsory as the default template is only capable of displaying string value. You may also use a Button instead of a HyperlinkButton, and the template overwriting is still recommended as it simplifies the layout of the Button control, if you don’t need the extra border and pressed/disabled effect of the Button control.

The ItemsControl does not virtualise UI elements in the list. This can have a memory impact if your list is large. If you are not planning using the LongListSelector (see later) in this case, I would recommend, if applicable, loading a limited number of items at load time and give the users a button at the end of the list to load more items:

Load Button At End of List

Deferred Load List Box

David Anson blogged about loading items in the ListBox with his DeferredLoadListBox. This is also a ListBox that uses StackPanel but delay creates the UI. The catch with DeferredLoadListBox is that it only defers the load. Once the UI elements are created, it does not virtualise them either. In fact, during my testing on a Samsung OMNIA 7, the over all scrolling performance of DeferredLoadListBox is even worse than the standard ListBox.

Long List Selector

The LongListSelector shipped in the SilverlightToolkit for WP7 is, in my opinion, the ideal control for loading large number of items when binding to a data collection. This control virtualise the UI items in the list to keep it’s memory footprint small. To put it simple, it destroys the UI elements when they are off the screen and recreates them when they are back in. You can set the BufferSize property to specify how many page’s items that you want to keep in memory. The default value is 1 page so UI items are (supposed to) destroyed immediately when they are out, you may test increasing this value to suit your app. Another interesting property is MaximumFlickVelocity. Decreasing this number can slow down the inertial scroll speed and buy some more time for the list to load it’s items. The default value is 4000. You may find 2500 a good number to start with if you need to tweak this.

It is also important to know that binding a collection that implements the IList interface (such as ObservableCollection) is much faster and resource friendly than those don’t, this is because you can also virtualise data using such a collection. Take a look at this page for more about UI and data virtualisation.

Pay very much attention to the Loaded event of items in your list especially when using the LongListSelector. This event is raised every time when an item is re-created. Which means handler of this event may get executed multiple times as you scroll the list. I would suggest keep away from the Loaded event of the items. If you really need to execute something on each load, consider doing it in a background thread. I have previously talked about this technique and limitation about it.

Lazy List Box

Last but not least, Peter Torr has implemented a very unique way to improve scrolling performance via his LazyListBox. His basic principle is “Don’t do work while the user is scrolling“. If you don’t want to read through his thoroughly explained blog, you can just download his sample code and copy out the LazyListBox class and start using it like a normal ListBox. The only change here would be, when you create the ItemTemplate for LazyListBox, you create a simpler (preferably no image) item for preview purpose. Because LazyListBox will show items using this template while it is scrolling. The real template is LoadedItemTemplate. LazyListBox will reload items in the current visible area with this template as soon as scrolling is stopped. Similar to LongListSelector, LazyListBox also virtualises off screen UI elements. However, you can optionally store some UI elements using the CachedItemTemplate to avoid heavy reloading activities. I suggest have a good read of his blog post if you really want to take LazyListBox as your solution.

What more to expect

After all, before Microsoft releases updates to improve the performance of the Silverlight run-time of WP7, there’s limited amount of optimisation that we can do.

When the WP7 developer tools first shipped Microsoft Silverlight Developer Jeff Wilcox tweeted invaluable performance tips for 3 straight hours. Tim Heuer collected Jeff’s tidbits and posted those on his blog.

While the list is fairly long, I’ve picked some obvious ones that are usually ignored by developers:

  • Pivot starts up faster than Panorama.
  • Always use JPEG over PNG if you can.
  • Set the compile option of image and media files as “Content” instead of “Resource” (in the property window).
  • Avoid using images larger than 2000 x 2000 pixels, not even the background of your Panorama.
  • The simpler your layout is, the faster the performance.
Advertisements

7 thoughts on “WP7: Everything About ListBox Performance

  1. kompir

    Do you have any tips on fast list filtering by text on WP7? So far I’ve tried using CollectionView, multiple lists (filtered and unfiltered), background workers. To no avail. My use case is like instant search in google – list filters as you type in a searchbox. I’d appreciate any tips.

  2. Leon

    @kompir
    what you are probably looking at could be an auto complete box. Have you tried the one that comes with the Silverlight Toolkit for Windows Phone 7?

  3. Mike

    Awesome article – thanks!! I’m trying to write a custom control that has a header and a listbox using items in an ObservableCollection. When the header is clicked I want the Listbox to be set to a height of 0 and then back to full height on the next header click (i.e: so it’s expandable and collapsable) but have been getting serious performance issues with ListBox so I’m hoping one of your many links will help me solve this!

  4. onmyway133

    @Leon: I have the same question as kompir. I want to filter listbox real time based on what user type in another TextBox. AutoCompleteBox is not suitable in my case. Do you have any advice ? Thanks

    • You can achieve this by doing the following:
      1. store the original list of items in your view model using a class level private field (e.g. _allItems).
      2. Create a public property of type IEnumerable that notifies property changed event (e.g. FilteredItems), and bind this to the list box’s items source.
      3. 2 way bind your search text box’s text property to a string property in your view model (e.g. SearchQuery). You can set the UpdateSourceTrigger to PropertyChanged if you need real time search result as the user types.
      4. Then when ever SearchQuery chagnes, you can do a LINQ query to filter your original list of item and assign the result to FilteredItems. This will trigger the list box to update it’s items source.

      The example view model looks like this:


      public class MyViewModel : INotifyPropertyChanged
      {
      private readonly IEnumerable _allItems;

      public MyViewModel(IEnumerable allItems)
      {
      FilteredItems = _allItems = allItems;
      }

      public string SearchQuery
      {
      get { return _searchQuery; }
      set
      {
      _searchQuery = value;
      FilteredItems(value);
      }
      }

      private string _searchQuery;

      public IEnumerable FilteredItems
      {
      get { return _filteredItems; }
      set
      {
      _filteredItems = value;
      OnPropertyChanged("FilteredItems");
      }
      }

      private IEnumerable _filteredItems;

      private void FilterItems(string query)
      {
      FilteredItems = _allItems.Where(item => item.Name.Contains(query));
      }
      }

      There are other techniques to improve performance in this case, such as executing the FilterItems method asynchronously, or start a timer when search query changes and execute FilterItems when the timer ticks.

      Hope this helps.

  5. Filippo

    Nice article!
    For example, in my app I’m loading about 100 UserCustomControls (which are pretty heavy) inside a StackPanel. Should I consider putting them in a LongListSelector, isn’t it?

    • Ever since the 7.5 Mango update, the list box performance has gotten much better with the sub-thread rendering technique. So if you are really experiencing performance issue on a real device then the long list selector might be the way to go, otherwise, just stick with the standard list box, it’s a much lighter control.

      BTW, you can also improve performance (but not significantly) by applying style/template to the items in the list box, instead of using user control. Style is cached in memory and the rendering engine is faster applying the cached style than parsing and initializing a new user control for each item in the list.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s