This article has been localized into Czech by the community.
Lenivé načítání (lazy loading) položek TreeView
Obvyklý proces při používání TreeView je vázat (bindovat) na kolekci položek nebo ručně přidávat každou úroveň ve stejném okamžiku. Nicméně v některých situacích chcete odložit načítání podřízených položek uzlu, dokud nejsou skutečně potřeba. To je obzvláště užitečné, pokud máte velmi hluboký strom s mnoha úrovněmi a podřízenými uzly, a skvělým příkladem toho je složková struktura vašeho počítače s Windows.
Každý disk na vašem počítači s Windows má řadu podsložek a každá z těchto podsložek má pod sebou další podsložky a tak dále. Projít každý disk a jeho podsložky by mohlo být extrémně časově náročné a váš TreeView by brzy obsahoval mnoho uzlů, přičemž vysoké procento z nich by nikdy nebylo potřeba. To je ideální úloha pro lenivě načítaný (lazy loading) TreeView, kde jsou podsložky načítány pouze na vyžádání.
Pro dosažení tohoto cíle jednoduše přidáme do každého disku nebo podsložky fiktivní složku a poté, když ji uživatel rozbalí, odstraníme fiktivní složku a nahradíme ji skutečnými hodnotami. Takto vypadá naše aplikace při spuštění - do té doby jsme získali pouze seznam dostupných disků na počítači:
Nyní můžete začít rozbalovat uzly a aplikace automaticky načte podřazené složky. Pokud je složka prázdná, zobrazí se jako prázdná, jakmile se ji pokusíte rozbalit, jak je vidět na dalším snímku obrazovky:
Jak je toho dosaženo? Pojďme se podívat na kód:
<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 je velmi jednoduchý a obsahuje pouze jeden zajímavý detail: Způsob, jakým se přihlašujeme k události Expanded u TreeViewItem. Všimněte si, že jde skutečně o TreeViewItem a ne o TreeView samotné, ale protože událost se šíří výše, můžeme ji zachytit na jednom místě pro celé TreeView, namísto toho, abychom se museli přihlašovat k ní pro každou položku, kterou do stromu přidáme. Tato událost je volána pokaždé, když je položka rozbalena, což musíme vzít v úvahu, abychom na vyžádání načetli její potomky.
V Code-behind začneme přidáním každého disku nalezeného na počítači do ovládacího prvku TreeView. Přiřadíme instanci DriveInfo k vlastnosti Tag, abychom ji mohli později načíst. Všimněte si, že používáme vlastní metodu pro vytvoření TreeViewItem, která se nazývá CreateTreeItem(), protože můžeme použít přesně stejnou metodu, když chceme později dynamicky přidat podsložku. Všimněte si v této metodě, jak přidáváme podsložky do kolekce Items ve formě řetězce s textem "Loading..." (Načítání).
Dalším krokem je událost TreeViewItem_Expanded. Jak již bylo zmíněno, tato událost je vyvolána pokaždé, když je položka TreeView rozbalena, takže první, co uděláme, je ověření, zda již byla tato položka načtena, a to kontrolou, zda potomci aktuálně obsahují pouze jeden prvek, kterým je řetězec - pokud ano, našli jsme potomka "Loading...", což znamená, že bychom nyní měli načíst skutečný obsah a nahradit s ním zástupný prvek.
Nyní použijeme vlastnost Tag položky k získání odkazu na instanci DriveInfo nebo DirectoryInfo, kterou daná položka reprezentuje, a poté získáme seznam podsložek, které přidáme do kliknuté položky, opět použitím metody CreateTreeItem(). Všimněte si, že smyčka, ve které přidáváme každou podsložku, je v bloku try..catch - to je důležité, protože některé cesty nemusí být přístupné, obvykle z bezpečnostních důvodů. Můžete zachytit výjimku a nějakým způsobem to odrážet v uživatelském rozhraní.
Shrnutí
Přihlášením k události Expanded můžeme snadno vytvořit lenivě načítaný (lazy loaded) TreeView, který může být v několika situacích mnohem lepším řešením než staticky vytvořený.