🕓 9 MIN Ever wondered how to …
No matter the purpose of your application or website, performance is essential to a great user experience. Slow loading apps and webpages will turn users away almost immediately. Fortunately, Lazy Loading is a technique used to optimize load time by only loading required content at first, and loading any remaining page content once the user needs it.Â
In this article, Johnny guides us through the steps required to implement the Lazy Loading functionality for ListView and Gridview controls in Uno Platform WASM applications.
Lazy Loading or incremental loading is a strategy to delay the loading of objects until they are actually needed. This can help reducing resources used by the application and increase loading times for the item collection, because only parts of the data are loaded initially.
For my usecase the trigger for loading more items is a certain scrolling threshold in the ListView
 and GridView
 control. To get the current scroll position we need to somehow access the ScrollView
 within the ListViewBase
 which is the base class for ListView
 and GridView
. We will use the VisualTreeHelper
 class to get information about nodes in the visual tree and get a reference to the inner ScrollView
 of the ListViewBase
var myScrollView = ExtendedVisualTreeHelper.GetFirstDescendant(myGridView);
myScrollView.ViewChanged += async (s, e) =>
{
if (myGridView.ItemsSource is not ISupportIncrementalLoading source) return;
if (myGridView.Items.Count > 0 && !source.HasMoreItems) return;
if (GetIsIncrementallyLoading(myGridView)) return;
if (((myScrollView.ExtentHeight - myScrollView.VerticalOffset) / myScrollView.ViewportHeight) - 1.0 <= loadingThreshold)
{
SetIsIncrementallyLoading(myGridView, true);
await source.LoadMoreItemsAsync(1);
}
}
We will add code to check for the need for lazy-loading whenever the ViewChanged
 event is fired – this happens when the user scrolls the collection.
In this code, we use the ExtentHeight
, VerticalOffset
 and ViewportHeight
 of the ScrollView
 to check if more than half of the scroll area was already scrolled, which will initiate the loading of more items.
Now we have the trigger when we want to load more items into our collection. For the actual fetch of the data we will use a special collection as the ItemsSource
of your ListViewBase
 control.
public class PaginatedCollection : ObservableCollection, ISupportIncrementalLoading
{
public delegate Task Fetch(int start, int count);
private readonly Fetch _fetch;
private int _start, _pageSize;
public PaginatedCollection(Fetch fetch, int pageSize)
{
_fetch = fetch;
_start = 0;
_pageSize = pageSize;
}
public IAsyncOperation LoadMoreItemsAsync(uint count)
{
return Task.Run(async () =>
{
var items = await _fetch(_start, _pageSize);
await CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
foreach (var item in items)
{
Add(item);
if (Count > _pageSize)
{
//hack to give the UI time for layout udpates
await Task.Delay(20);
}
}
});
_start += items.Length;
return new LoadMoreItemsResult() { Count = (uint)items.Length };
}).AsAsyncOperation();
}
public bool HasMoreItems => true;
}
The PaginatedCollection
 derives from ObservableCollection
 which already has mechanisms for UI updates via databinding if the collection changes. I have added a Fetch
 object to the class which can be any method responsible for getting more data (i.e. a call to a backend service). The fetch is called in LoadMoreItemsAsync
 which itself is triggered if the scrolling threshold is reached (see extension classes above). The collection has a _pageSize
 field which defines the number of items to be loaded.
The last part for using lazy-loading is to set the ItemsSource
 of the control to a PaginatedCollection
 and add the AddIncrementallyLoadingSupport="True"
 attribute to the control.
local:ListViewExtensions.AddLazyLoadingSupport="True"
Let’s create an Items collection and set the page size accordingly. For the demo app i’m simulating a data fetch by adding a Task.Delay
 to the method.
void LoadGrid()
{
Items = new PaginatedCollection- (
async (start, size) =>
{
var response = await FetchItems(start, size);
return response;
},
pageSize: 25
);
}
async Task
- FetchItems(int start, int size)
{
//simulates some work to get new items
await Task.Delay(200);
return _allItems.Skip(start).Take(size).ToArray();
}
I have tested the lazy-loading solution with ListView
 and GridView
 controls and in both cases the loading performance and responsiveness of the collection could be increased significantly. I also found out that ListView
 seems to be optimized already and didn’t benefit as much as GridView
. I’m comparing the lazy-loading approach to controls with standard ObservableCollection
 where all items are loaded initally. The Item
 model is just a record with a name and a random image url.
public record Item(string Name, string Image);
These values are displayed in a StackPanel
 within the DataTemplate
 of the ItemsControl
. For testing i can set the count of the collection and trigger loading of each ItemsControl
 separately.
You can see in the recording that the regular GridView
 even blocks the UI when the loading starts. Note that this sample is running in InterpretedAndAOT
 mode – more info below.
This post is just about lazy-loading for ListView
 and GridView
 – but there is more you can do to improve the performance of Uno WebAssembly apps. First of all, it makes a big difference what’s the runtime execution mode of your application. It can be set via the WasmShellMonoRuntimeExecutionMode
 element in the .csproj file of the WebAssembly project. There are three different values for the element.
Interpreter
InterpreterAndAOT
FullAOT
Interpreter
 is the default value and has the fastest build times but the slowest performance, since all the code is interpreted during runtime. For better perfomance you want to try some form of AOT compilation for your project. You could go with FullAOT
 to compile all assemblies to wasm binaries Ahead of Time. This has the best performance in regards to code execution but produces the largest application package. For my usecase the InterpreterAndAOT
 mode is a nice compromise, it allows mix AOT compilation and interpreted code.
Most of the tips and tricks for ListViewBase
 are explained in the |documentation. Depending on your usecase it might help to experiment with List
 vs. ObservableCollection
 as the type of the ItemsSource
. It’s also worth noting that you can set your custom styles for the ItemTemplate
 of the ItemsControl
. Especially when using GridView
, replacing the default GridViewItem
style can increase performance a lot, this is also mentioned in the Uno Platform documentation.
If you are new to Uno Platform, the best way to get started is to follow our official getting started guide. (5 min to complete)
For better discoverability and ease of adopting Uno Platform we have brushed up and published various working examples for Uno Platform, ranging from small single-feature samples to larger showcase applications. Check it all out and expect many more to come in the future.
Tags:
🕓 9 MIN Ever wondered how to …
🕓 3 MIN State management patterns like …
🕓 3 MIN We are introducing over …
Uno Platform
360 rue Saint-Jacques, suite G101,
Montréal, Québec, Canada
H2Y 1P5
USA/CANADA toll free: +1-877-237-0471
International: +1-514-312-6958
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.
Uno Platform 5.2 LIVE Webinar – Today at 3 PM EST – Watch