使用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属性来获取对当前项目所代表的DriveInfo或DirectoryInfo实例的引用,然后我们再次使用CreateTreeItem()方法获取我们添加到所单击项目的子目录列表。 请注意,我们添加每个子文件夹的循环位于try..catch块中 - 这很重要,因为某些路径可能无法访问,通常是出于安全原因。 您可以获取异常并使用它以某种方式在界面中反映这一点。
通过订阅Expanded事件,我们可以轻松地创建一个延迟加载的TreeView,这可能是一个某些情况下比静态创建的更好的解决方案。
(c) 2024 chaojicainiao.com MIT license