Load On Demand in WinUI TreeView

The TreeView allows you to load child items only when they are requested using Load on-demand(Lazy load). It helps to load the child items from services when end-user expands the node. Initially populate the root Nodes by assigning ItemsSource and then when any node is expanded, child items can be loaded using LoadOnDemandCommand. The Load on-demand is applicable for bound mode only.

<Page x:Class="syncfusion.treeviewdemos.winui.LoadOnDemandPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:syncfusion="using:Syncfusion.UI.Xaml.TreeView"
      xmlns:local="using:syncfusion.treeviewdemos.winui"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
      NavigationCacheMode="Disabled">

<Page.DataContext>
    <local:LoadOnDemandViewModel />
</Page.DataContext>

<Grid>
    <syncfusion:SfTreeView x:Name="treeView"
                           BorderBrush="LightGray"
                           BorderThickness="1"
                           SelectionMode="Multiple"
                           ExpandActionTrigger="Node"
                           IsAnimationEnabled="True"
                           LoadOnDemandCommand="{Binding TreeViewOnDemandCommand}"
                           ItemsSource="{Binding Menu}">
                    <syncfusion:SfTreeView.ItemTemplate>
                        <DataTemplate>
                            <TextBlock VerticalAlignment="Center"
                                       Text="{Binding ItemName}" />
                        </DataTemplate>
                    </syncfusion:SfTreeView.ItemTemplate>
    </syncfusion:SfTreeView>
</Grid>
</Page>
public class LoadOnDemandViewModel
{
    private ObservableCollection<LoadOnDemandModel> menu;
   
    private DelegateCommand treeViewOnDemandCommand;

    public LoadOnDemandViewModel()
    {
        this.Menu = GetMenuItems();
        TreeViewOnDemandCommand = new DelegateCommand(ExecuteOnDemandLoading, CanExecuteOnDemandLoading);
    }

    public ObservableCollection<LoadOnDemandModel> Menu
    {
        get { return menu; }
        internal set { menu = value; }
    }

    public DelegateCommand TreeViewOnDemandCommand
    {
        get { return treeViewOnDemandCommand; }
        set { treeViewOnDemandCommand = value; }
    }

    public static IEnumerable<LoadOnDemandModel> GetSubMenu(int iD)
    {
        ObservableCollection<LoadOnDemandModel> menuItems = new ObservableCollection<LoadOnDemandModel>();
        if (iD == 1)
        {
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Hot Singles", HasChildNodes = false, ID = 11 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Rising Artists", HasChildNodes = false, ID = 12 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Live Music", HasChildNodes = false, ID = 13 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "More in Music", HasChildNodes = true, ID = 14 });
        }
        else if (iD == 2)
        {
            menuItems.Add(new LoadOnDemandModel() { ItemName = "100 Albums - $10 Each", HasChildNodes = false, ID = 21 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Hip-Hop and R&B Sale", HasChildNodes = false, ID = 22 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "CD Deals", HasChildNodes = false, ID = 23 });
        }
        else if (iD == 3)
        {
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Songs", HasChildNodes = false, ID = 31 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Bestselling Albums", HasChildNodes = false, ID = 32 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "New Releases", HasChildNodes = false, ID = 33 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "MP3 Albums", HasChildNodes = false, ID = 34 });
        }
        else if (iD == 4)
        {
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Rock Music", HasChildNodes = false, ID = 41 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Gospel", HasChildNodes = false, ID = 42 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Latin Music", HasChildNodes = false, ID = 43 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Jazz", HasChildNodes = false, ID = 44 });
        }
        else if (iD == 5)
        {
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Hunger Games", HasChildNodes = false, ID = 51 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Pride and Prejudice", HasChildNodes = false, ID = 52 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Harry Potter", HasChildNodes = false, ID = 53 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Game of Thrones", HasChildNodes = false, ID = 54 });
        }
        else if (iD == 14)
        {
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Music Trade-In", HasChildNodes = false, ID = 141 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Redeem a Gift Card", HasChildNodes = false, ID = 142 });
            menuItems.Add(new LoadOnDemandModel() { ItemName = "Band T-Shirts", HasChildNodes = false, ID = 143 });
        }
        return menuItems;
    }

    private bool CanExecuteOnDemandLoading(object sender)
    {
        var haschildnodes = ((sender as TreeViewNode).Content as LoadOnDemandModel).HasChildNodes;
        if (haschildnodes)
        {
            if ((sender as TreeViewNode).ChildNodes.Count > 0)
                return false;
            else
                return true;
        }
        else
            return false;
    }

    private async void ExecuteOnDemandLoading(object obj)
    {
        var node = obj as TreeViewNode;
        node.ShowExpanderAnimation = true;
        LoadOnDemandModel loadOnDemandModel = node.Content as LoadOnDemandModel;
        await Application.Current.Resources.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => 
        {
            await Task.Delay(2000).ConfigureAwait(true);
            var items = GetSubMenu(loadOnDemandModel.ID);
            node.PopulateChildNodes(items);
            if (items.Any())
                node.IsExpanded = true;
            node.ShowExpanderAnimation = false;
        });
    }

    private static ObservableCollection<LoadOnDemandModel> GetMenuItems()
    {
        ObservableCollection<LoadOnDemandModel> menuItems = new ObservableCollection<LoadOnDemandModel>();
        menuItems.Add(new LoadOnDemandModel() { ItemName = "Discover Music", HasChildNodes = true, ID = 1 });
        menuItems.Add(new LoadOnDemandModel() { ItemName = "Sales and Events", HasChildNodes = true, ID = 2 });
        menuItems.Add(new LoadOnDemandModel() { ItemName = "Categories", HasChildNodes = true, ID = 3 });
        menuItems.Add(new LoadOnDemandModel() { ItemName = "MP3 Albums", HasChildNodes = true, ID = 4 });
        menuItems.Add(new LoadOnDemandModel() { ItemName = "Fiction Book Lists", HasChildNodes = true, ID = 5 });
        return menuItems;
    }
}
public class LoadOnDemandModel : NotificationObject
{
    private string itemName;

    private int id;

    private bool hasChildNodes;

    public string ItemName
    {
        get { return itemName; }
        set
        {
            itemName = value;
            RaisePropertyChanged(nameof(ItemName));
        }
    }

    public int ID
    {
        get { return id; }
        set
        {
            id = value;
            RaisePropertyChanged(nameof(ID));
        }
    }

    public bool HasChildNodes
    {
        get { return hasChildNodes; }
        set
        {
            hasChildNodes = value;
            RaisePropertyChanged(nameof(HasChildNodes));
        }
    }
}

NOTE

The LoadOnDemandCommand receives TreeViewNode as command parameter by default.

Handling expander visibility

The TreeView shows the expander for a particular node based on return value of CanExecute method of LoadOnDemandCommand. If CanExecute returns true, then expander icon is displayed for that node. If CanExecute returns false, then expander icon will not be displayed for that node. The CanExecute method gets called to decide the visibility of expander icon and before executing LoadOnDemandCommand.

private bool CanExecuteOnDemandLoading(object sender)
{
    var haschildnodes = ((sender as TreeViewNode).Content as LoadOnDemandModel).HasChildNodes;
    if (haschildnodes)
    {
        if ((sender as TreeViewNode).ChildNodes.Count > 0)
            return false;
        else
            return true;
    }
    else
        return false;
}

On-demand loading of child items

You can load child items for the node in Execute method of LoadOnDemandCommand. The Execute method will get called when user expands the tree node. In LoadOnDemand.Execute method, you can perform following operations,

  • Show or hide busy indicator in the place of expander by setting TreeViewNode.ShowExpanderAnimation until the data fetched.
  • Once data fetched, you can populate the child nodes by calling TreeViewNode.PopulateChildNodes method by passing the child items collection.
  • When load on-demand command executes expanding operation will not be handled by TreeView. So, you have to set TreeViewNode.IsExpanded property to true to expand the tree node after populating child nodes.
  • You can skip population of child items again and again when every time the node expands, based on TreeViewNode.ChildNodes count.
private async void ExecuteOnDemandLoading(object obj)
{
    var node = obj as TreeViewNode;
    node.ShowExpanderAnimation = true;
    LoadOnDemandModel loadOnDemandModel = node.Content as LoadOnDemandModel;
    await Application.Current.Resources.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => 
    {
        await Task.Delay(2000).ConfigureAwait(true);
        var items = GetSubMenu(loadOnDemandModel.ID);
        node.PopulateChildNodes(items);
        if (items.Any())
            node.IsExpanded = true;
        node.ShowExpanderAnimation = false;
    });
}

WinUI TreeView with Load On-Demand

NOTE

View sample in GitHub