How to Implement Lazy Loading for WPF Treeview Elements
Loading a large amount of data in WPF TreeView can be expensive in terms of time & resources. But a little trick, i.e. Lazy Loading, can be used to load data quickly and with fewer resources.
Our WPF TreeView can be easily customized to use Lazy Loading, where it can load the child nodes on demand when they are requested. When the parent node is expanded, the child nodes can be loaded from services, databases, etc.
For example, if your application loads products for multiple categories, it will take a lot of time to load all the products and categories if the count is very large. In such cases, the best way to load child nodes is to perform lazy loading when the parent nodes are expanded.
In this article, we implement lazy loading with ComponentOne's WPF TreeView in just two steps, with the use case of loading ComponentOne controls available for different platforms:
- Creating a TreeView
- Implementing On-Demand loading for TreeViewItems
Creating a TreeView
We will create a TreeView loaded with ComponentOne supported platforms in this step. Let's start by creating a C1TreeView instance inside XAML as follows:
<c1:C1TreeView x:Name="treeView"
Margin="5"
SelectionMode="Single"
IsVirtualizing="True"
SnapsToDevicePixels="True"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<c1:C1TreeView.ItemTemplate>
<DataTemplate>
<Grid Height="40">
<TextBlock Text="{Binding Name}"
Margin="8"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Grid.Column="1"
FontFamily="Segoe UI"/>
</Grid>
</DataTemplate>
</c1:C1TreeView.ItemTemplate>
</c1:C1TreeView>
Now we will initialize our TreeView by adding a root node which will contain all the platforms (parent nodes) like so:
private void LoadData()
{
_viewModel.LoadData();
// Gets the platforms supported by ComponentOne
var platforms = _viewModel.GetPlatforms();
treeView.Items.Clear();
// Creating root node
var rootNode = new C1TreeViewItem()
{
IsExpanded = true,
FontSize = 16
};
treeView.Items.Add(rootNode);
foreach (var platform in platforms)
{
// Adding platforms to root node
rootNode.Items.Add(CreatePlatformNode(platform));
}
}
In the above code, you can observe that we are creating & adding parent nodes using the CreatePlatformNode method. We will discuss it in brief in the next step. In short, this method configures the platform node to load its children when it is expanded.
Implementing On-Demand loading for TreeViewItems
Now we have our TreeView ready and loaded with ComponentOne platforms. In this step, we will implement Lazy Loading with TreeView to load controls when the platform node is expanded.
Creating Lazy Load Provider
We will begin by creating a provider that will enable C1TreeViewItems to load their children on demand as follows:
public abstract class C1TreeViewLazyLoadProvider
{
...
public void LoadOnDemand(C1TreeViewItem parent, object data)
{
// Add a dummy node so this node can be expanded
parent.Items.Add(new C1TreeViewItem());
/// Hook up event handler for lazy loading support.
parent.Expanding += OnParentNodeExpanding;
/// Stores the information to be used for lazy loading.
_lazyLoadInfo[parent] = data;
}
private async void OnParentNodeExpanding(object? sender, SourcedEventArgs e)
{
var parent = sender as C1TreeViewItem;
/// Unhook event handler after items are loaded.
parent.Expanding -= OnParentNodeExpanding;
await OnRequestingData(parent, _lazyLoadInfo[parent]);
}
protected abstract Task OnRequestingData(C1TreeViewItem parent, object data);
}
In the above code, you can see that we are registering the C1TreeViewItem's (Parent) Expanding event inside the LoadOnDemand method and unregistering it when the handler (OnParentNodeExpanding) is invoked so that the children are loaded only once. You can modify it as per your requirements.
You can also observe that there is an abstract method named OnRequestingData. This method gets invoked when the C1TreeViewItem is expanded, and it will contain the logic for loading the child items.
Implement lazy loading for platform controls by using the above C1TreeViewLazyLoadProvider, as shown below:
public class ControlsLazyLoader : C1TreeViewLazyLoadProvider
{
...
protected override async Task OnRequestingData(C1TreeViewItem parent, object data)
{
_loadIndicator.Visibility = Visibility.Visible;
var platformId = (string)data;
await Task.Delay(1000); // Simulate Delay
// Gets the data from data source
var controls = await _viewModel.GetControls(platformId);
// Remove any dummy item
parent.Items.Clear();
// Set controls collection as ItemsSource
parent.ItemsSource = controls;
parent.Foreground = Brushes.Black;
_loadIndicator.Visibility = Visibility.Collapsed;
}
}
Configuring Lazy Loading for TreeViewItems
Finally, we will configure our parent nodes to support the lazy loading of their children. I hope you remember the CreatePlatformNode method discussed in the first step, as this is when that method comes into the picture. The CreatePlatformNode method enables the lazy loading for parent items using the ControlsLazyLoader implemented in the previous step like so:
private C1TreeViewItem CreatePlatformNode(Platform platform)
{
var node = new C1TreeViewItem()
{
Margin = new Thickness(20, 0, 0, 0),
IsExpanded = false,
Foreground = Brushes.Gray
};
node.Header = new TextBlock() { Text = platform.Name, Padding = new Thickness(10) };
// setting item template for children
node.ItemTemplate = treeView.ItemTemplate;
// Loads children on demand
_controlsLoader.LoadOnDemand(node, platform.Id);
return node;
}
That's it! Implementing lazy loading for WPF C1TreeView is complete.
In this blog, implemented lazy loading with ComponentOne's WPF TreeView. You can also learn more about C1TreeView features here.
Please feel free to share if you have any comments or questions.