Categories: WPF 教程

WPF教程之 延迟加载TreeView项目

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,这可能是一个某些情况下比静态创建的更好的解决方案。

冒牌SEO

前端开发者,欢迎大家一起沟通和交流。

Share
Published by
冒牌SEO

Recent Posts

自定义指令:聊聊vue中的自定义指令应用法则

今天我们来聊聊vue中的自定义…

1 天 ago

聊聊Vue中@click.stop和@click.prevent

一起来学下聊聊Vue中@cli…

1 周 ago

Nginx 基本操作:启动、停止、重启命令。

我们来学习Nginx基础操作:…

2 周 ago

Vue3:手动清理keep-alive组件缓存的方法

Vue3中手动清理keep-a…

2 周 ago

聊聊React和Vue组件更新的实现及区别

React 和 Vue 都是当…

3 周 ago