Update GridView and ListView items incrementally (XAML)

When a ListView or GridView control has a lot of items and the user scrolls quickly through them, fully displaying each item's contents can use a lot of UI resources. This can lead to a somewhat choppy UI experience. Depending on how fast the user scrolls, many new items can temporarily appear blank or missing. When this happens, the user may not be sure whether any more items remain. The user has to wait until the UI fully shows new items, if any more remain, before they can be viewed. Windows 8.1 improves this item display experience through placeholders and incremental data template updates.

Placeholders

In Windows 8.1, when the user scrolls quickly through a ListView or GridView control that has a lot of items, the UI by default shows a temporary placeholder for each next item until the UI can fully show it. This gives the user a visual hint that there are more items yet to fully display. These placeholders can also make the ListView or GridView appear faster to the user.

Placeholders are turned on by default for Windows Store apps that run on Windows 8.1. Placeholders can also be explicitly turned on by setting the ListView or GridView control's ShowsScrollingPlaceholders property to true. To explicitly turn off placeholders, set the ListView or GridView control's ShowsScrollingPlaceholders property to false.

Incremental data template updates

In addition to placeholders, you can show portions of an item in multiple stages. For example, for a list of movies, you could show the movies' titles in the first stage. Then you could show the movies' ratings in the second stage. You could show a picture of each movie poster in the third stage, and so on. After each stage, the user knows more quickly about each movie and can select one of them before all of the movies' details are shown.

Windows 8.1 can't know by default which portion of an item is most important for your app to display first, which to display next, and so on. To set this behavior, use the ListView or GridView control's ContainerContentChanging event to determine what portion of the items to display at each stage. Incremental data template updates are available for all Windows Store apps that are compiled to target Windows 8.1 and that use the ContainerContentChanging event.

To explore this behavior, create a new Microsoft Visual C# project based on the Blank App (XAML) template, and add this code and markup.

  1. Add a class named MyItem that represents a simulated set of items.

    namespace LotsOfItems
    {
        public class MyItem
        {
            public string Title { get; set; }
            public string Subtitle { get; set; }
            public string Description { get; set; }
    
            public MyItem (string title, string subtitle, string description)
            {
                Title = title;
                Subtitle = subtitle;
                Description = description;
            }
        }
    }
    
  2. Add Extensible Application Markup Language (XAML) markup to the default MainPage.xaml file to show a grid view and an item display template. Set the GridView control's ContainerContentChanging event. Bind the item display template controls to the simulated items' properties, such as Title, Subtitle, and Description.

    <Page
            x:Class="LotsOfItems.MainPage"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="using:LotsOfItems"
            xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="d">
        <Grid>
            <GridView x:Name="myGridView"
                      ItemsSource="{Binding}"
                      Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
                      ContainerContentChanging="MyGridView_ContainerContentChanging">           
                <GridView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Height="100" 
                                    Width="100" 
                                    Background="Blue">
                            <Rectangle x:Name="placeholderRectangle" 
                                       Fill="Red" 
                                       Opacity="0"/>
                            <TextBlock x:Name="titleTextBlock" 
                                       Text="{Binding Title}" 
                                       Foreground="Yellow"/>
                            <TextBlock x:Name="subtitleTextBlock" 
                                       Text="{Binding Subtitle}" 
                                       Foreground="Aqua"/>
                            <TextBlock x:Name="descriptionTextBlock" 
                                       Text="{Binding Description}" 
                                       Foreground="Gray"/>
                        </StackPanel>
                    </DataTemplate>
                </GridView.ItemTemplate>
            </GridView>
        </Grid>   
    </Page>
    
  3. Add code to the default MainPage.xaml.cs file to create the simulated set of items. Connect the GridView control to the list of items, and respond to the GridView control's ContainerContentChanging event as the user quickly scrolls through the grid view.

    using System;
    using System.Collections.Generic;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Navigation;
    using Windows.UI.Xaml.Shapes;
    
    namespace LotsOfItems
    {
        public sealed partial class MainPage : Page
        {
            List<MyItem> myItems = new List<MyItem>(); 
    
            public MainPage()
            {
                CreateTestItems();
                this.InitializeComponent();
            }
    
            // Create a simulated list of 150,000 items.
            void CreateTestItems()
            {
                for (int i = 1; i < 150000; i++)
                {
                    myItems.Add(new MyItem(
                        "Title:" + i.ToString(), // Title.
                        "Sub:" + i.ToString(), // Subtitle.
                        "Desc:" + i.ToString())); // Description.                            
                }
            }
    
            // Connect the grid view to the list of items.
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                myGridView.ItemsSource = myItems;            
            }
    
            // Display each item incrementally to improve performance.
            private void MyGridView_ContainerContentChanging(
                    ListViewBase sender, 
                    ContainerContentChangingEventArgs args)
            {
                args.Handled = true;
    
                if (args.Phase != 0)
                {
                    throw new Exception("Not in phase 0.");
                }
    
                // First, show the items' placeholders.
                StackPanel templateRoot = 
                    (StackPanel)args.ItemContainer.ContentTemplateRoot;
                Rectangle placeholderRectangle = 
                    (Rectangle)templateRoot.FindName("placeholderRectangle");
                TextBlock titleTextBlock = 
                    (TextBlock)templateRoot.FindName("titleTextBlock");
                TextBlock subtitleTextBlock =
                    (TextBlock)templateRoot.FindName("subtitleTextBlock");
                TextBlock descriptionTextBlock =
                    (TextBlock)templateRoot.FindName("descriptionTextBlock");
    
                // Make the placeholder rectangle opaque.
                placeholderRectangle.Opacity = 1;
    
                // Make everything else invisible.
                titleTextBlock.Opacity = 0;
                subtitleTextBlock.Opacity = 0;
                descriptionTextBlock.Opacity = 0;
    
                // Show the items' titles in the next phase.
                args.RegisterUpdateCallback(ShowTitle);
            } 
    
            // Show the items' titles.
            private void ShowTitle(
                    ListViewBase sender, 
                    ContainerContentChangingEventArgs args)
            {
                if (args.Phase != 1)
                {
                    throw new Exception("Not in phase 1.");
                }
    
                // Next, show the items' titles. Keep everything else invisible.
                MyItem myItem = (MyItem)args.Item;
                SelectorItem itemContainer = 
                    (SelectorItem)args.ItemContainer;
                StackPanel templateRoot = 
                    (StackPanel)itemContainer.ContentTemplateRoot;
                TextBlock titleTextBlock = 
                    (TextBlock)templateRoot.FindName("titleTextBlock");
    
                titleTextBlock.Text = myItem.Title;
                titleTextBlock.Opacity = 1;
    
                // Show the items' subtitles in the next phase.
                args.RegisterUpdateCallback(ShowSubtitle);
            }
    
            // Show the items' subtitles.
            private void ShowSubtitle(
                    ListViewBase sender, 
                    ContainerContentChangingEventArgs args)
            {
                if (args.Phase != 2)
                {
                    throw new Exception("Not in phase 2.");
                }
    
                // Next, show the items' subtitles. Keep everything else invisible.
                MyItem myItem = (MyItem)args.Item;
                SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
    
                StackPanel templateRoot = 
                    (StackPanel)itemContainer.ContentTemplateRoot;
                TextBlock subtitleTextBlock = 
                    (TextBlock)templateRoot.FindName("subtitleTextBlock");
    
                subtitleTextBlock.Text = myItem.Subtitle;
                subtitleTextBlock.Opacity = 1;
    
                // Show the items' descriptions in the next phase.
                args.RegisterUpdateCallback(ShowDescription);
            }
    
            // Show the items' descriptions.
            private void ShowDescription(
                    ListViewBase sender, 
                    ContainerContentChangingEventArgs args)
            {
                if (args.Phase != 3)
                {
                    throw new Exception("Not in phase 3.");
                }
    
                // Finally, show the items' descriptions. 
                MyItem myItem = (MyItem)args.Item;
                SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
    
                StackPanel templateRoot = 
                    (StackPanel)itemContainer.ContentTemplateRoot;
                Rectangle placeholderRectangle = 
                    (Rectangle)templateRoot.FindName("placeholderRectangle");
                TextBlock descriptionTextBlock =
                    (TextBlock)templateRoot.FindName("descriptionTextBlock");
    
                descriptionTextBlock.Text = myItem.Description;
                descriptionTextBlock.Opacity = 1;
    
                // Make the placeholder rectangle invisible.
                placeholderRectangle.Opacity = 0;
            }
        }
    }
    
  4. Run the app. Scroll quickly through the grid view. Notice that as new items appear on the screen, the items' titles appear first, followed by the subtitles, followed by the descriptions.

Note  For additional code that shows how to call the ContainerContentChanging event, see the XAML ListView and GridView essentials sample.

 

Load, store, and display large sets of data efficiently

Improve startup time for apps with GridView and ListView controls