Skip to main content Skip to footer

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:

  1. Creating a TreeView
  2. 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.

lazy loading

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.

Tags:

comments powered by Disqus