Loading a large amount of data by the default procedure in our WPF TreeView control can take time and consume many resources. But a little-known trick in Windows Presentation Framework (WPF) is lazy loading, which gives you the ability to load data on demand quickly and without using expensive resources to hold all the data.
The WPF TreeView supports lazy loading, where TreeView loads the child nodes on demand when they are requested. The child nodes can be loaded from services or databases when the parent node is expanded. For example, if you are developing an application like the file explorer TreeView in Windows, a lot of time will be taken when you loop all the drives and files. In such cases, the best way to load the child nodes is to perform lazy loading (load on demand) when the child nodes are expanded.
In this article, we are going to implement lazy loading using MVVM pattern in just three steps, with the use case of a Windows file browser:
- Create a TreeView and bind it with data.
- Create load-on-demand command in the ViewModel.
- Bind the LoadOnDemandCommand of TreeView with the command created in the ViewModel.
Creating a TreeView and binding it with data
Create a class named Directory with the property Name, which denotes the name of the drive or directory, and the property FullName, which denotes the full path of the directory. The Directory class should be derived from NotificationObject, which has the implementation for the INotifyPropertyChanged interface.
Model
public class Directory : NotificationObject { private string name; private bool hasChildNodes; private string fullName; public string Name { get { return name; } set { name = value; RaisePropertyChanged("Name"); } } public string FullName { get { return fullName; } set { fullName = value; RaisePropertyChanged("FullName"); } } public bool HasChildNodes { get { return hasChildNodes; } set { hasChildNodes = value; RaisePropertyChanged("HasChildNodes"); } } }
Next, create a ViewModel class with the Directories property. This property holds the driver details of the system to create a file-exploring TreeView. When a user expands the driver, the child nodes will be loaded on demand.
ViewModel
public class ViewModel { public ObservableCollection<Directory> Directories { get; set; } public ViewModel() { this.Directories = this.GetDrives(); } private ObservableCollection<Directory> GetDrives() { ObservableCollection<Directory> directories = new ObservableCollection<Directory>(); DriveInfo[] driveInfos = DriveInfo.GetDrives(); foreach (DriveInfo driveInfo in driveInfos) { directories.Add(new Directory() { Name = driveInfo.Name, FullName = driveInfo.Name, HasChildNodes = true }); } return directories; } }
After creating the ViewModel class:
- Add the ViewModel class as DataContext for Window.
- Bind the Directories property from the ViewModel to the ItemsSource property of TreeView.
- Set the ItemTemplate property of TreeView to display the content. Here, Label is added, and the Content of Label is bound to the Name
XAML
<Window.DataContext> <local:ViewModel/> </Window.DataContext> <Syncfusion:SfTreeView ItemsSource="{Binding Directories}" ItemHeight="30" HorizontalAlignment="Left" IsAnimationEnabled="True" Margin="25,0,0,0" VerticalAlignment="Top" Width="250"> <Syncfusion:SfTreeView.ItemTemplate> <DataTemplate> <Label VerticalContentAlignment="Center" Content="{Binding Name}" FocusVisualStyle="{x:Null}" /> </DataTemplate> </Syncfusion:SfTreeView.ItemTemplate> </Syncfusion:SfTreeView>
Creating load-on-demand command in the ViewModel
Now, TreeView will be displayed with two nodes. Let’s enable lazy loading in TreeView by creating LoadOnDemandCommand. This will control the expander visibility based on the return value of the CanExecute command. The loading of child nodes is done in the Execute command.
public class ViewModel { public ICommand TreeViewLoadOnDemandCommand { get; set; } public ViewModel() { TreeViewLoadOnDemandCommand = new DelegateCommand(ExecuteOnDemandLoading, CanExecuteOnDemandLoading); } }
Handling TreeNode expander visibility in the CanExecute method of command
Return true from the CanExecute method to show the expander for the node if the node has child nodes, or else return false. Here, the visibility of the expander is returned based on the HasChildNodes property in the Directory. The HasChildNodes property is set based on whether the directory has subdirectories or files when the node is populated.
private bool CanExecuteOnDemandLoading(object sender) { var hasChildNodes = ((sender as TreeViewNode).Content as Directory).HasChildNodes; if (hasChildNodes) return true; else return false; }
Lazy loading of TreeView in Execute method of command
Load the child nodes in the Execute method. Call the Execute method when a user expands the tree node.
Show or hide the busy indicator in the place of the expander by setting TreeViewNode.ShowExpanderAnimation until the data is fetched.
Retrieve the inner directories and files from the GetDirectories method. Populate the child nodes by calling the TreeViewNode.PopulateChildNodes method by passing the child items collection that is obtained from GetDirectories.
During command execution, the expanding operation will not be handled by TreeView. So, you have to set the TreeViewNode.IsExpanded property to true to expand the tree node after populating child nodes.
You should skip the population of child items every time the node expands, based on the TreeViewNode.ChildNodes count.
private void ExecuteOnDemandLoading(object obj) { var node = obj as TreeViewNode; // Skip the repeated population of child items every time the node expands. if (node.ChildNodes.Count > 0) { node.IsExpanded = true; return; } //Animation starts for expander to show progress of load on demand. node.ShowExpanderAnimation = true; Directory Directory = node.Content as Directory; Application.Current.MainWindow.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(async () => { await Task.Delay(1000); //Fetching child items to add. var items = GetDirectories(Directory); // Populating child items for the node in on demand. node.PopulateChildNodes(items); if (items.Count() > 0) //Expand the node after child items are added. node.IsExpanded = true; //Stop the animation after load on demand is executed. If animation is not stopped, it remains after execution of load on demand. node.ShowExpanderAnimation = false; })); } public IEnumerable<Directory> GetDirectories(Directory directory) { var directories = new ObservableCollection<Directory>(); var dirInfo = new DirectoryInfo(directory.FullName); foreach (DirectoryInfo directoryInfo in dirInfo.GetDirectories()) { try { directories.Add(new Directory() { Name = directoryInfo.Name, HasChildNodes = directoryInfo.GetDirectories().Length > 0 || directoryInfo.GetFiles().Length > 0, FullName = directoryInfo.FullName }); } catch { } } foreach (FileInfo fileInfo in dirInfo.GetFiles()) { directories.Add(new Directory() { Name = fileInfo.Name, HasChildNodes = false, FullName = fileInfo.FullName }); } return directories; }
Binding load on-demand command of TreeView with ViewModel command
Finally, bind the TreeView.LoadOnDemandCommand property to the ViewModel’s TreeViewLoadOnDemandCommand property.
That’s all. Lazy loading implementation is completely done for WPF TreeView.
<Syncfusion:SfTreeView ItemsSource="{Binding Directories}" LoadOnDemandCommand="{Binding TreeViewLoadOnDemandCommand}" ItemHeight="30" HorizontalAlignment="Left" IsAnimationEnabled="True" Margin="25,0,0,0" VerticalAlignment="Top" Width="250"> <Syncfusion:SfTreeView.ItemTemplate> <DataTemplate> <Label VerticalContentAlignment="Center" Content="{Binding Name}" FocusVisualStyle="{x:Null}" /> </DataTemplate> </Syncfusion:SfTreeView.ItemTemplate> </Syncfusion:SfTreeView>
![Lazy loading in Syncfusion WPF TreeView](https://www.syncfusion.com/blogs/wp-content/uploads/2021/02/Lazy-loading-in-Syncfusion-WPF-TreeView.gif)
Resources
The complete project explained in this blog is available in this GitHub repository. Also, our demos are now available in the Microsoft Store, and our .NET Core demos are available in the App Center.
Conclusion
I hope, you enjoyed this blog about the procedure to perform lazy loading in the Syncfusion WPF TreeView control using MVVM pattern. You can learn more about WPF TreeView’s features here.
Syncfusion has over 90 essential WPF controls like DataGrid, Charts, Diagram, and PDF Viewer. Give these controls a shot by downloading the setup from our latest release.
For existing customers, the newest version is available for download from the license and downloads page. If you are not yet a customer, you can try our 30-day free trial to check out these new features.
Also, if you wish to send us feedback or would like to submit any questions, please feel free to post them in the comments section of this blog post. You can also contact us through our support forums, feedback portal, or Direct-Trac support system.