TOC

This article has been localized into Chinese by the community.

TreeView控件:

延迟加载TreeView项目

使用TreeView时通常是绑定到项目集合或一次手动添加每个级别。 但是,在某些情况下,您希望延迟加载节点子项,直到实际需要它们为止。 如果您有一个非常深的树具有许多级别和子节点,这将特别有用,这是一个很好的例子,这是Windows计算机的文件夹结构。

Windows计算机上的每个驱动器都有一系列子文件夹,每个子文件夹下又有子文件夹,依此类推。 循环遍历每个驱动器和每个驱动器子文件夹可能会变得非常耗时,并且TreeView会包含很多节点,其中很大一部分节点从不需要。 这是TreeView延迟加载的完美任务,子文件夹仅在需要的时候加载。

为此,我们只需在每个驱动器或子文件夹中添加一个伪文件夹,然后当用户展开它时,我们删除伪文件夹并将其替换为实际值。 这就是我们的应用程序启动时的样子 - 到那时,我们只获得了计算机上可用驱动器的列表:

您现在可以开始展开节点,应用程序将自动加载子文件夹。 如果文件夹为空,一旦您尝试展开它,它将显示为空,如下一个屏幕截图所示:

那它是如何做到的? 我们来看看代码:

<Window x:Class="WpfTutorialSamples.TreeView_control.LazyLoadingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="LazyLoadingSample" Height="300" Width="300">
    <Grid>
        <TreeView Name="trvStructure" TreeViewItem.Expanded="TreeViewItem_Expanded" Margin="10" />
    </Grid>
</Window>
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;

namespace WpfTutorialSamples.TreeView_control
{
	public partial class LazyLoadingSample : Window
	{
		public LazyLoadingSample()
		{
			InitializeComponent();
			DriveInfo[] drives = DriveInfo.GetDrives();
			foreach(DriveInfo driveInfo in drives)
				trvStructure.Items.Add(CreateTreeItem(driveInfo));
		}

		public void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
		{
			TreeViewItem item = e.Source as TreeViewItem;
			if((item.Items.Count == 1) && (item.Items[0] is string))
			{
				item.Items.Clear();

				DirectoryInfo expandedDir = null;
				if(item.Tag is DriveInfo)
					expandedDir = (item.Tag as DriveInfo).RootDirectory;
				if(item.Tag is DirectoryInfo)
					expandedDir = (item.Tag as DirectoryInfo);
				try
				{
					foreach(DirectoryInfo subDir in expandedDir.GetDirectories())
						item.Items.Add(CreateTreeItem(subDir));
				}
				catch { }
			}
		}

		private TreeViewItem CreateTreeItem(object o)
		{
			TreeViewItem item = new TreeViewItem();
			item.Header = o.ToString();
			item.Tag = o;
			item.Items.Add("Loading...");
			return item;
		}
	}
}

XAML非常简单,只需要关注一个细节:我们订阅TreeViewItem的Expanded事件的方式。 请注意,这确实是TreeViewItem而不是TreeView本身,因为是冒泡事件,我们能够在一个地方捕获整个TreeView,而不必为我们添加到树中的每个项目(item)订阅它。 每次展开项目(item)时都会调用此事件,我们需要按需加载其子项目(item)。

后置代码中,我们首先将计算机上找到的每个驱动器添加到TreeView控件中。 我们将DriveInfo实例分配给Tag属性,以便稍后检索它。 请注意,我们通过调用自定义方法CreateTreeItem()来创建TreeViewItem,因为我们可以在以后动态添加子文件夹时使用完全相同的方法。 请注意,在此方法中,我们如何将子项添加到Items集合中,其形式为文本是“Loading ...”的字符串。

接下来是TreeViewItem_Expanded事件。 如前所述,每次展开TreeView项时都会引发此事件,因此我们要做的第一件事就是通过检查子项当前是否只包含一个项(并且是字符串)来检查是否已加载此项。 - 如果是这样,我们找到了“Loading ...”子项,这意味着我们现在应该加载实际内容并用它替换占位符项。

我们现在使用项目Tag属性来获取对当前项目所代表的DriveInfoDirectoryInfo实例的引用,然后我们再次使用CreateTreeItem()方法获取我们添加到所单击项目的子目录列表。 请注意,我们添加每个子文件夹的循环位于try..catch块中 - 这很重要,因为某些路径可能无法访问,通常是出于安全原因。 您可以获取异常并使用它以某种方式在界面中反映这一点。

小结

通过订阅Expanded事件,我们可以轻松地创建一个延迟加载的TreeView,这可能是一个某些情况下比静态创建的更好的解决方案。


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!