Incremental loading Quickstart for Windows Store apps using C# and XAML
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
From: Developing a Windows Store business app using C#, XAML, and Prism for the Windows Runtime
Learn how to add incremental loading capabilities to a GridView or ListView to create a more responsive and useful UI when the user scrolls through large data sets.
Download
You will learn
- How to add the IncrementalUpdateBehavior to a XAML control in an item template.
- How to handle the ContainerContentChanging event and define in code-behind what happens during each rendering phase.
Applies to
- Windows Runtime for Windows 8.1
- C#
- Extensible Application Markup Language (XAML)
This Quickstart demonstrates how to add incremental loading capabilities to a GridView or ListView. However, use of incremental loading techniques in most cases do not significantly impact the total loading time for all the items compared to simple data binding. The benefit that incremental loading provides is to make the items usable sooner, by first displaying just enough item data to enable the user to decide whether they are interested in the item or not.
Building and running the Quickstart
Build the Quickstart as you would a standard project:
- On the Microsoft Visual Studio menu bar, choose Build > Build Solution.
- After you build the project, you must deploy it. On the menu bar, choose Build > Deploy Solution. Visual Studio also deploys the project when you run the app from the debugger.
- After you deploy the project, pick the Quickstart tile to run the app. Alternatively, from Visual Studio, on the menu bar, choose Debug > Start Debugging.
When the app runs you will see a page similar to the one shown in the following diagram.
Each button demonstrates a different approach to incremental loading using the same data set and GridView template. The Data Binding button is provided for comparison purposes. Press the button and then scroll rapidly through the items to observe how the rendering experience differs with each approach.
[Top]
Solution structure
The IncrementalLoadingQuickstart Visual Studio solution uses Visual Studio solution folders to organize the source code into these logical categories:
- The Assets folder contains the item images.
- The Common folder contains the auto-generated helper classes used for navigation and app lifecycle management.
- The SampleData folder contains the non-image data that is used to populate the items.
In the main folder, the DataBindingScenario.* files show basic data binding with no incremental loading. The BlendBehaviorScenario.* files show the IncrementalUpdateBehavior and the CCCWithCodeScenario.* files show the handling of the ContainerContentChanging event. The ItemViewer.* files define a user control that we use to represent each item in the GridView. The ItemViewer class contains the methods that are called in response to ContainerContentChanging events in the second scenario described below.
[Top]
Using the IncrementalUpdateBehavior to add incremental loading
To use the IncrementalUpdateBehavior, you must first add a reference to the Behaviors SDK (XAML). In Solution Explorer, right click on the References node and in the left pane, choose Extensions and in the middle pane, check the Behaviors SDK option.
Next, add the Microsoft.Xaml.Interactivity and Microsoft.Xaml.Interactions.Core namespaces to the root Page element.
IncrementalLoadingQuickstart\BlendBehaviorScenario.xaml
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
All that is left to do is to attach the behaviors to the controls and assign a phase number to each behavior, as shown in the following code example.
IncrementalLoadingQuickstart\BlendBehaviorScenario.xaml
<Interactivity:Interaction.Behaviors>
<Core:IncrementalUpdateBehavior Phase="2"/>
</Interactivity:Interaction.Behaviors>
In this example three phases are defined. As a general rule, three phases is the maximum number because it shouldn't take more two or three phases to display enough content to make the item usable, and in the phase after that you might as well just render the rest of the item template. The following example shows the entire item template for the GridView, with behaviors attached to each element. Note that multiple elements can be assigned the same phase number.
IncrementalLoadingQuickstart\BlendBehaviorScenario.xaml
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<Grid>
<Image Source="ms-appx:///Assets/placeHolderImage.png" Height="100" Width="60" VerticalAlignment="Center" Margin="0,0,10,0"/>
<Image Source="{Binding ImageUri}" Height="100" Width="60" VerticalAlignment="Center" Margin="0,0,10,0">
<Interactivity:Interaction.Behaviors>
<Core:IncrementalUpdateBehavior Phase="3"/>
</Interactivity:Interaction.Behaviors>
</Image>
</Grid>
<StackPanel Margin="0,0,0,0" Orientation="Vertical">
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" Foreground="{StaticResource ApplicationForegroundThemeBrush}" FontSize="14.667" FontWeight="Light" Width="100" VerticalAlignment="Center" HorizontalAlignment="Left" FontFamily="Segoe UI">
<Interactivity:Interaction.Behaviors>
<Core:IncrementalUpdateBehavior Phase="1"/>
</Interactivity:Interaction.Behaviors>
</TextBlock>
<TextBlock Text="{Binding Category}" TextWrapping="Wrap" Foreground="{StaticResource ApplicationForegroundThemeBrush}" FontSize="14.667" FontWeight="Light" Width="100" MaxHeight="20" VerticalAlignment="Center" HorizontalAlignment="Left">
<Interactivity:Interaction.Behaviors>
<Core:IncrementalUpdateBehavior Phase="2"/>
</Interactivity:Interaction.Behaviors>
</TextBlock>
<HyperlinkButton Content="{Binding Link}" NavigateUri="{Binding Link}">
<Interactivity:Interaction.Behaviors>
<Core:IncrementalUpdateBehavior Phase="2"/>
</Interactivity:Interaction.Behaviors>
</HyperlinkButton>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
The following diagram shows what happens when a user scrolls quickly as the items are loading. In this image, phases 1 and 2 have completed on all the items, and phase 3 is about to start.
The effect will typically be more noticeable on ARM devices.
Handling the ContainerContentChanging event in code-behind
If the IncrementalUpdateBehavior approach does not provide you with the control or performance you need, then you can try handling the ContainerContentChanging event that is raised by the ListViewBase class whenever it is called up to re-render its content, for example in response to the user scrolling to the right or left. For a full tutorial on how to handle this event, see Update GridView and ListView items incrementally.
The first step is to add the event to the XAML GridView element.
IncrementalLoadingQuickstart\CCCWithCodeScenario.xaml
ContainerContentChanging="ItemGridView_ContainerContentChanging"
In the event handler, we first check whether the data is in the "recycle queue." This means that the container is being reused, and we clear the contents before setting it again in the various phases. The current phase is passed in the ContainerContentChangingEventArgs argument. For each phase, we call a method that changes the opacity of the item elements in various ways to achieve the desired effect. If the opacity of an element is set to zero, the XAML engine will not bother to render it at all, which of course speeds up that phase. Note also that in the first two phases, a callback is registered that tells the XAML engine to call this handler again when the next phase begins.
IncrementalLoadingQuickstart\CCCWithCodeScenario.xaml.cs
void ItemGridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
ItemViewer iv = args.ItemContainer.ContentTemplateRoot as ItemViewer;
if (args.InRecycleQueue == true)
{
iv.ClearData();
}
else if (args.Phase == 0)
{
iv.ShowPlaceholder(args.Item as Item);
// Register for async callback to visualize Title asynchronously
args.RegisterUpdateCallback(ContainerContentChangingDelegate);
}
else if (args.Phase == 1)
{
iv.ShowTitle();
iv.ShowImagePlaceHolder();
args.RegisterUpdateCallback(ContainerContentChangingDelegate);
}
else if (args.Phase == 2)
{
iv.ShowCategory();
iv.ShowLinkbutton();
args.RegisterUpdateCallback(ContainerContentChangingDelegate);
}
else if (args.Phase == 3)
{
iv.ShowImage();
}
// For improved performance, set Handled to true since app is visualizing the data item
args.Handled = true;
}
The following example shows the methods that are called from the event handler and which perform the work of adjusting the opacity levels on the elements. An Opacity value of zero means the element will not be rendered at all and a value of one means that it will be rendered completely opaque, with no blending over the background. You can also set Opacity to intermediate levels between 0 and 1, but doing so will not help to speed up rendering.
IncrementalLoadingQuickstart\ItemViewer.xaml.cs
public void ShowPlaceholder(Item item)
{
_item = item;
titleTextBlock.Opacity = 0;
categoryTextBlock.Opacity = 0;
image.Opacity = 0;
linkButton.Opacity = 0;
}
/// <summary>
/// Visualize the Title by updating the TextBlock for Title and setting Opacity
/// to 1.
/// </summary>
public void ShowTitle()
{
titleTextBlock.Text = _item.Title;
titleTextBlock.Opacity = 1;
}
public void ShowLinkbutton()
{
linkButton.Content = _item.Link;
linkButton.NavigateUri = new System.Uri(_item.Link);
linkButton.Opacity = 1;
}