Udostępnij za pośrednictwem


Widok drzewa

Kontrolka widoku drzewa pozwala na hierarchiczną listę z węzłami, które mogą być rozwijane i zwijane, a które zawierają zagnieżdżone elementy. Może służyć do zilustrowania struktury folderów lub zagnieżdżonych relacji w interfejsie użytkownika.

Widok drzewa używa kombinacji wcięcia i ikon do reprezentowania zagnieżdżonej relacji między węzłami nadrzędnymi i węzłami podrzędnymi. Zwinięte węzły używają strzałki wskazującej w prawo, a rozwinięte węzły używają strzałki wskazującej w dół.

Ikona strzałki w TreeView

Aby reprezentować węzły, możesz dołączyć ikonę w szablonie danych elementu widoku drzewa. Jeśli na przykład zostanie wyświetlona hierarchia systemu plików, możesz użyć ikon folderów dla nadrzędnych notatek i ikon plików dla węzłów liścia.

Ikony trójkąta i folderów w widoku TreeView

Interfejsy API TreeView obsługują następujące funkcje:

  • Zagnieżdżanie na poziomie N
  • Wybór jednego lub wielu węzłów
  • Powiązanie danych z właściwością ItemsSource w elementach TreeView i TreeViewItem
  • TreeViewItem jako węzeł główny elementu szablonu TreeView
  • Dowolne typy zawartości w obiekcie TreeViewItem
  • Przeciąganie i upuszczanie między widokami drzewa

Czy jest to właściwa kontrola?

  • Użyj widoku TreeView, gdy elementy mają zagnieżdżone elementy listy oraz ważne jest, aby zilustrować hierarchiczną relację elementów z elementami równorzędnymi i węzłami.

  • Należy unikać używania elementu TreeView, jeśli wyróżnianie zagnieżdżonej relacji elementu nie jest priorytetem. W przypadku większości scenariuszy zgłębiania szczegółowego odpowiedni jest zwykły widok listy.

Tworzenie widoku drzewa

Widok drzewa można utworzyć przez powiązanie elementu ItemsSource z hierarchicznym źródłem danych lub samodzielne tworzenie obiektów TreeViewNode i zarządzanie nimi.

Aby utworzyć widok drzewa, należy użyć kontrolki TreeView i hierarchii obiektów TreeViewNode . Hierarchia węzłów jest tworzona przez dodanie co najmniej jednego węzła głównego do kolekcji RootNodes kontrolki TreeView. Każdy TreeViewNode może mieć więcej węzłów dodanych do kolekcji Dzieci. Węzły widoku drzewa można zagnieżdżać do dowolnej wymaganej głębokości.

Możesz powiązać hierarchiczne źródło danych z właściwością ItemsSource, aby udostępnić zawartość widoku drzewa, podobnie jak w przypadku właściwości ItemsSource pochodnej ListView. Podobnie użyj elementu ItemTemplate (i opcjonalnego elementu ItemTemplateSelector), aby udostępnić element DataTemplate renderujący element.

Ważne

ElementSource i powiązane interfejsy API wymagają systemu Windows 10 w wersji 1809 (SDK 17763) lub nowszej lub WinUI 2.

ItemsSource to alternatywny mechanizm wprowadzania zawartości do kontrolki TreeView.RootNodes. Nie można jednocześnie ustawić ItemsSource i RootNodes. Gdy używasz elementu ItemsSource, węzły są tworzone dla Ciebie i można uzyskać do nich dostęp z właściwości TreeView.RootNodes .

Oto przykład prostego widoku drzewa zadeklarowanego w języku XAML. Zazwyczaj węzły są dodawane w kodzie, ale w tym miejscu pokazujemy hierarchię XAML, ponieważ może to być przydatne do wizualizacji sposobu tworzenia hierarchii węzłów.

<muxc:TreeView>
    <muxc:TreeView.RootNodes>
        <muxc:TreeViewNode Content="Flavors"
                           IsExpanded="True">
            <muxc:TreeViewNode.Children>
                <muxc:TreeViewNode Content="Vanilla"/>
                <muxc:TreeViewNode Content="Strawberry"/>
                <muxc:TreeViewNode Content="Chocolate"/>
            </muxc:TreeViewNode.Children>
        </muxc:TreeViewNode>
    </muxc:TreeView.RootNodes>
</muxc:TreeView>

W większości przypadków widok drzewa wyświetla dane ze źródła danych, więc zazwyczaj deklarujesz główną kontrolkę TreeView w języku XAML, a obiekty TreeViewNode dodajesz w kodzie lub przy użyciu powiązania danych.

Wiązanie z hierarchicznym źródłem danych

Aby utworzyć widok drzewa przy użyciu powiązania danych, ustaw hierarchiczną kolekcję na właściwość TreeView.ItemsSource . Następnie w elemencie ItemTemplate ustaw kolekcję elementów podrzędnych na właściwość TreeViewItem.ItemsSource .

<muxc:TreeView ItemsSource="{x:Bind DataSource}">
    <muxc:TreeView.ItemTemplate>
        <DataTemplate x:DataType="local:Item">
            <muxc:TreeViewItem ItemsSource="{x:Bind Children}"
                               Content="{x:Bind Name}"/>
        </DataTemplate>
    </muxc:TreeView.ItemTemplate>
</muxc:TreeView>

Aby uzyskać pełny kod, zobacz Widok drzewa przy użyciu powiązania danych .

Elementy i pojemniki na elementy

Jeśli używasz TreeView.ItemsSource, te API są dostępne do pobierania węzła lub elementu danych z kontenera i odwrotnie.

TreeViewItem (Element widoku) Opis
TreeView.ItemFromContainer Pobiera element danych dla określonego kontenera TreeViewItem .
TreeView.ContainerFromItem Pobiera kontener TreeViewItem dla określonego elementu danych.
Węzeł TreeViewNode (Węzeł TreeView) Opis
TreeView.NodeFromContainer Pobiera TreeViewNode dla określonego kontenera TreeViewItem.
TreeView.ContainerFromNode (Węzeł) Pobiera kontener TreeViewItem dla określonego elementu TreeViewNode.

Zarządzanie węzłami widoku drzewa

Ten widok drzewa jest taki sam jak ten utworzony wcześniej w języku XAML, ale węzły są tworzone w kodzie.

<muxc:TreeView x:Name="sampleTreeView"/>
private void InitializeTreeView()
{
    muxc.TreeViewNode rootNode = new muxc.TreeViewNode() { Content = "Flavors" };
    rootNode.IsExpanded = true;
    rootNode.Children.Add(new muxc.TreeViewNode() { Content = "Vanilla" });
    rootNode.Children.Add(new muxc.TreeViewNode() { Content = "Strawberry" });
    rootNode.Children.Add(new muxc.TreeViewNode() { Content = "Chocolate" });

    sampleTreeView.RootNodes.Add(rootNode);
}
Private Sub InitializeTreeView()
    Dim rootNode As New muxc.TreeViewNode With {.Content = "Flavors", .IsExpanded = True}
    With rootNode.Children
        .Add(New muxc.TreeViewNode With {.Content = "Vanilla"})
        .Add(New muxc.TreeViewNode With {.Content = "Strawberry"})
        .Add(New muxc.TreeViewNode With {.Content = "Chocolate"})
    End With
    sampleTreeView.RootNodes.Add(rootNode)
End Sub

Te API są dostępne do zarządzania hierarchią danych w twoim widoku drzewa.

Widok drzewa Opis
Węzły główne Widok drzewa może mieć jeden lub więcej węzłów głównych. Dodaj obiekt TreeViewNode do kolekcji RootNodes , aby utworzyć węzeł główny. Element nadrzędny węzła głównego ma zawsze wartość null. Głębokość węzła głównego wynosi 0.
Węzeł TreeViewNode (Węzeł TreeView) Opis
Dzieci Dodaj obiekty TreeViewNode do kolekcji Dzieci węzła rodzica, aby utworzyć hierarchię węzłów. Węzeł jest rodzicem wszystkich węzłów w kolekcji dzieci.
HasChildren (Dzieci) true, jeśli węzeł posiada podrzędne węzły. false wskazuje pusty folder lub element.
MaNieurzeczywistnioneDzieci Użyj tej właściwości, jeśli wypełniasz węzły podczas ich rozszerzania. Zobacz Fill a node when it's expanding later in this article (Wypełnianie węzła po rozwinięciu w dalszej części tego artykułu).
Głębokość Wskazuje, jak daleko od węzła głównego znajduje się węzeł podrzędny.
Rodzic Pobiera węzeł TreeViewNode, który jest właścicielem kolekcji Dzieci, do której należy ten węzeł.

Widok drzewa używa właściwości HasChildren i HasUnrealizedChildren , aby określić, czy jest wyświetlana ikona rozwijania/zwijania. Jeśli którakolwiek z właściwości ma wartość true, ikona zostanie wyświetlona; w przeciwnym razie nie zostanie wyświetlona.

Zawartość węzła widoku drzewa

Element danych reprezentowany przez węzeł widoku drzewa można przechowywać we właściwości Content .

W poprzednich przykładach zawartość była prostą wartością ciągu. W tym miejscu węzeł widoku drzewa reprezentuje folder Obrazy użytkownika, więc biblioteka obrazów StorageFolder jest przypisana do właściwości Content węzła.

StorageFolder picturesFolder = KnownFolders.PicturesLibrary;
muxc.TreeViewNode pictureNode = new muxc.TreeViewNode();
pictureNode.Content = picturesFolder;
Dim picturesFolder As StorageFolder = KnownFolders.PicturesLibrary
Dim pictureNode As New muxc.TreeViewNode With {.Content = picturesFolder}

Uwaga / Notatka

Aby uzyskać dostęp do folderu Obrazy , należy określić funkcję Biblioteka obrazów w manifeście aplikacji. Aby uzyskać więcej informacji, zobacz Deklaracje możliwości aplikacji .

Możesz podać element DataTemplate , aby określić sposób wyświetlania elementu danych w widoku drzewa.

Uwaga / Notatka

W systemie Windows 10 w wersji 1803 należy ponownie utworzyć szablon kontrolki TreeView i określić niestandardowy element ItemTemplate , jeśli zawartość nie jest ciągiem. W nowszych wersjach ustaw właściwość ItemTemplate . Aby uzyskać więcej informacji, zobacz TreeView.ItemTemplate.

Styl kontenera elementów

Niezależnie od tego, czy używasz ItemsSource czy RootNodes, rzeczywisty element służący do wyświetlania każdego węzła (nazywany "kontenerem") jest elementem TreeViewItem. Właściwości TreeViewItem można modyfikować, aby stylizować kontener przy użyciu właściwości ItemContainerStyle lub ItemContainerStyleSelector elementu TreeView.

W tym przykładzie pokazano, jak zmienić rozwinięte/zwinięte glify na pomarańczowe znaki +/-. W domyślnym szablonie TreeViewItem glyphs są ustawione na używanie czcionki Segoe MDL2 Assets . Właściwość Setter.Value można ustawić, podając wartość znaku Unicode w formacie używanym przez XAML, w następujący sposób: Value="&#xE948;".

<muxc:TreeView>
    <muxc:TreeView.ItemContainerStyle>
        <Style TargetType="muxc:TreeViewItem">
            <Setter Property="CollapsedGlyph" Value="&#xE948;"/>
            <Setter Property="ExpandedGlyph" Value="&#xE949;"/>
            <Setter Property="GlyphBrush" Value="DarkOrange"/>
        </Style>
    </muxc:TreeView.ItemContainerStyle>
    <muxc:TreeView.RootNodes>
        <muxc:TreeViewNode Content="Flavors"
               IsExpanded="True">
            <muxc:TreeViewNode.Children>
                <muxc:TreeViewNode Content="Vanilla"/>
                <muxc:TreeViewNode Content="Strawberry"/>
                <muxc:TreeViewNode Content="Chocolate"/>
            </muxc:TreeViewNode.Children>
        </muxc:TreeViewNode>
    </muxc:TreeView.RootNodes>
</muxc:TreeView>

Selektory szablonów elementów

Domyślnie w widoku TreeView jest wyświetlana reprezentacja ciągu elementu danych dla każdego węzła. Możesz ustawić właściwość ItemTemplate , aby zmienić, co jest wyświetlane dla wszystkich węzłów. Możesz też użyć elementu ItemTemplateSelector , aby wybrać inną pozycję DataTemplate dla elementów widoku drzewa na podstawie typu elementu lub innych kryteriów, które określisz.

Na przykład w aplikacji eksploratora plików można użyć jednego szablonu danych dla folderów i innego dla plików.

Foldery i pliki korzystające z różnych szablonów danych

Oto przykład tworzenia i używania selektora szablonu elementu. Aby uzyskać więcej informacji, zobacz klasę DataTemplateSelector .

Uwaga / Notatka

Ten kod jest częścią większego przykładu i nie będzie działał samodzielnie. Aby zobaczyć pełny przykład, w tym kod, który definiuje ExplorerItem, zapoznaj się z repozytorium Xaml-Controls-Gallery w witrynie GitHub. TreeViewPage.xaml i TreeViewPage.xaml.cs zawierają odpowiedni kod.

<Page.Resources>
    <DataTemplate x:Key="FolderTemplate" x:DataType="local:ExplorerItem">
        <muxc:TreeViewItem ItemsSource="{x:Bind Children}">
            <StackPanel Orientation="Horizontal">
                <Image Width="20" Source="Assets/folder.png"/>
                <TextBlock Text="{x:Bind Name}" />
            </StackPanel>
        </muxc:TreeViewItem>
    </DataTemplate>

    <DataTemplate x:Key="FileTemplate" x:DataType="local:ExplorerItem">
        <muxc:TreeViewItem>
            <StackPanel Orientation="Horizontal">
                <Image Width="20" Source="Assets/file.png"/>
                <TextBlock Text="{x:Bind Name}"/>
            </StackPanel>
        </muxc:TreeViewItem>
    </DataTemplate>

    <local:ExplorerItemTemplateSelector
            x:Key="ExplorerItemTemplateSelector"
            FolderTemplate="{StaticResource FolderTemplate}"
            FileTemplate="{StaticResource FileTemplate}" />
</Page.Resources>

<Grid>
    <muxc:TreeView
        ItemsSource="{x:Bind DataSource}"
        ItemTemplateSelector="{StaticResource ExplorerItemTemplateSelector}"/>
</Grid>
public class ExplorerItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate FolderTemplate { get; set; }
    public DataTemplate FileTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        var explorerItem = (ExplorerItem)item;
        if (explorerItem.Type == ExplorerItem.ExplorerItemType.Folder) return FolderTemplate;

        return FileTemplate;
    }
}

Typ obiektu przekazanego do metody SelectTemplateCore zależy od tego, czy tworzysz widok drzewa, ustawiając właściwość ItemsSource , czy przez samodzielne tworzenie obiektów TreeViewNode i zarządzanie nimi.

  • Jeśli element ItemsSource jest ustawiony, obiekt będzie mieć dowolny typ elementu danych. W poprzednim przykładzie obiekt był ExplorerItem, więc można go było użyć po prostym rzutowaniu do ExplorerItem: var explorerItem = (ExplorerItem)item;.
  • Jeśli element ItemsSource nie został ustawiony i samodzielnie zarządzasz węzłami widoku drzewa, obiekt przekazany do elementu SelectTemplateCore jest węzłem TreeViewNode. W takim przypadku możesz pobrać element danych z właściwości TreeViewNode.Content .

Oto selektor szablonu danych z pokazanego później przykładu widoku drzewa Biblioteki obrazów i muzyki. Metoda SelectTemplateCore odbiera węzeł TreeViewNode, który może mieć folder StorageFolder lub StorageFile jako jego zawartość. Na podstawie zawartości można zwrócić szablon domyślny lub określony szablon folderu muzycznego, folder obrazu, pliki muzyczne lub pliki obrazów.

protected override DataTemplate SelectTemplateCore(object item)
{
    var node = (TreeViewNode)item;
    if (node.Content is StorageFolder)
    {
        var content = node.Content as StorageFolder;
        if (content.DisplayName.StartsWith("Pictures")) return PictureFolderTemplate;
        if (content.DisplayName.StartsWith("Music")) return MusicFolderTemplate;
    }
    else if (node.Content is StorageFile)
    {
        var content = node.Content as StorageFile;
        if (content.ContentType.StartsWith("image")) return PictureItemTemplate;
        if (content.ContentType.StartsWith("audio")) return MusicItemTemplate;
    }
    return DefaultTemplate;
}
Protected Overrides Function SelectTemplateCore(ByVal item As Object) As DataTemplate
    Dim node = CType(item, muxc.TreeViewNode)

    If TypeOf node.Content Is StorageFolder Then
        Dim content = TryCast(node.Content, StorageFolder)
        If content.DisplayName.StartsWith("Pictures") Then Return PictureFolderTemplate
        If content.DisplayName.StartsWith("Music") Then Return MusicFolderTemplate
    ElseIf TypeOf node.Content Is StorageFile Then
        Dim content = TryCast(node.Content, StorageFile)
        If content.ContentType.StartsWith("image") Then Return PictureItemTemplate
        If content.ContentType.StartsWith("audio") Then Return MusicItemTemplate
    End If

    Return DefaultTemplate
End Function

Interakcja z widokiem drzewa

Widok drzewa można skonfigurować tak, aby umożliwić użytkownikowi interakcję z nim na kilka różnych sposobów:

  • Rozwiń lub zwiń węzły
  • Pojedyncze lub wielokrotne wybieranie elementów
  • Kliknij, aby wywołać element

Rozwiń/Zwiń

Każdy węzeł widoku drzewa, który ma elementy podrzędne, można zawsze rozwinąć lub zwinąć, klikając ikonę rozwiń/zwiń. Można również programowo rozwinąć lub zwinąć węzeł i odpowiedzieć, gdy węzeł zmieni stan.

Programowe rozwijanie/zwijanie węzła

Istnieją 2 sposoby rozwijania lub zwijania węzła widoku drzewa w kodzie.

  • Klasa TreeView ma metody Zwiń i Rozwiń . Podczas wywoływania tych metod należy przekazać element TreeViewNode , który chcesz rozwinąć lub zwinąć.

  • Każdy węzeł TreeViewNode ma właściwość IsExpanded . Za pomocą tej właściwości można sprawdzić stan węzła lub ustawić go w celu zmiany stanu. Tę właściwość można również ustawić w języku XAML, aby ustawić początkowy stan węzła.

Wypełnianie węzła podczas rozszerzania

Może być konieczne wyświetlenie dużej liczby węzłów w widoku drzewa lub nie jesteś pewien, ile węzłów będzie. Kontrolka TreeView nie jest zwirtualizowana, dlatego można zarządzać zasobami, wypełniając każdy węzeł podczas jego rozszerzania oraz usuwając węzły podrzędne, gdy jest zwinięty.

Obsłuż zdarzenie Rozszerzanie i użyj właściwości HasUnrealizedChildren, aby dodać elementy podrzędne do węzła podczas jego rozszerzania. Właściwość HasUnrealizedChildren wskazuje, czy węzeł musi zostać wypełniony, czy też kolekcja Dzieci została już wypełniona. Należy pamiętać, że właściwość TreeViewNode nie ustawia tej wartości, musisz zarządzać nią w kodzie aplikacji.

Oto przykład używanych interfejsów API. Pełny przykładowy kod na końcu tego artykułu zawiera kontekst, w tym implementację fillTreeNode.

private void SampleTreeView_Expanding(muxc.TreeView sender, muxc.TreeViewExpandingEventArgs args)
{
    if (args.Node.HasUnrealizedChildren)
    {
        FillTreeNode(args.Node);
    }
}
Private Sub SampleTreeView_Expanding(sender As muxc.TreeView, args As muxc.TreeViewExpandingEventArgs)
    If args.Node.HasUnrealizedChildren Then
        FillTreeNode(args.Node)
    End If
End Sub

Nie jest to wymagane, ale możesz także obsłużyć zdarzenie Collaps i usuwać węzły podrzędne po zamknięciu węzła nadrzędnego. Może to być ważne, jeśli widok drzewa zawiera wiele węzłów lub jeśli dane węzła używają wielu zasobów. Należy rozważyć wpływ na wydajność wypełniania węzła za każdym razem, gdy jest otwierany, w porównaniu z pozostawieniem zamkniętych węzłów potomnych. Najlepsza opcja będzie zależeć od aplikacji.

Oto przykład procedury obsługi zwiniętego zdarzenia.

private void SampleTreeView_Collapsed(muxc.TreeView sender, muxc.TreeViewCollapsedEventArgs args)
{
    args.Node.Children.Clear();
    args.Node.HasUnrealizedChildren = true;
}
Private Sub SampleTreeView_Collapsed(sender As muxc.TreeView, args As muxc.TreeViewCollapsedEventArgs)
    args.Node.Children.Clear()
    args.Node.HasUnrealizedChildren = True
End Sub

Wywoływanie elementu

Użytkownik może wywołać akcję (traktując element jak przycisk), zamiast wybierać element. Aby odpowiedzieć na tę interakcję użytkownika, obsługujesz zdarzenie ItemInvoked.

Uwaga / Notatka

W przeciwieństwie do ListView, która ma właściwość IsItemClickEnabled, wywoływanie elementów jest zawsze dostępne w widoku drzewa. Nadal możesz wybrać, czy chcesz obsługiwać zdarzenie, czy nie.

klasa TreeViewItemInvokedEventArgs

ElementWwołane args zdarzenia zapewniają dostęp do wywoływanego elementu. Właściwość InvokedItem ma wywoływany węzeł. Możesz rzutować go do elementu TreeViewNode i pobrać element danych z właściwości TreeViewNode.Content .

Oto przykład procedury obsługi zdarzeń itemInvoked . Element danych jest elementem IStorageItem, a w tym przykładzie wyświetlane są tylko informacje o pliku i drzewie. Ponadto jeśli węzeł jest węzłem folderu, rozwija lub zwija węzeł w tym samym czasie. W przeciwnym razie węzeł rozwija się lub zwija tylko wtedy, gdy kliknie się strzałkę.

private void SampleTreeView_ItemInvoked(muxc.TreeView sender, muxc.TreeViewItemInvokedEventArgs args)
{
    var node = args.InvokedItem as muxc.TreeViewNode;
    if (node.Content is IStorageItem item)
    {
        FileNameTextBlock.Text = item.Name;
        FilePathTextBlock.Text = item.Path;
        TreeDepthTextBlock.Text = node.Depth.ToString();

        if (node.Content is StorageFolder)
        {
            node.IsExpanded = !node.IsExpanded;
        }
    }
}
Private Sub SampleTreeView_ItemInvoked(sender As muxc.TreeView, args As muxc.TreeViewItemInvokedEventArgs)
    Dim node = TryCast(args.InvokedItem, muxc.TreeViewNode)
    Dim item = TryCast(node.Content, IStorageItem)
    If item IsNot Nothing Then
        FileNameTextBlock.Text = item.Name
        FilePathTextBlock.Text = item.Path
        TreeDepthTextBlock.Text = node.Depth.ToString()
        If TypeOf node.Content Is StorageFolder Then
            node.IsExpanded = Not node.IsExpanded
        End If
    End If
End Sub

Wybór elementu

Kontrolka TreeView obsługuje zarówno wybór pojedynczy, jak i wybór wielokrotny. Domyślnie wybór węzłów jest wyłączony, ale można ustawić właściwość TreeView.SelectionMode , aby zezwolić na wybór węzłów. Wartości TreeViewSelectionMode to Brak, Pojedynczy, i Wielokrotny.

Wybór wielokrotny

Po włączeniu wielokrotnego zaznaczenia obok każdego węzła widoku drzewa jest wyświetlane pole wyboru, a zaznaczone elementy są wyróżnione. Użytkownik może wybrać lub odznaczyć element przy użyciu pola wyboru; Kliknięcie elementu nadal powoduje jego wywołanie.

Wybranie lub usunięcie zaznaczenia węzła nadrzędnego spowoduje zaznaczenie lub usunięcie zaznaczenia wszystkich elementów podrzędnych tego węzła. Jeśli niektóre, ale nie wszystkie elementy podrzędne w węźle nadrzędnym są zaznaczone, pole wyboru węzła nadrzędnego jest wyświetlane w stanie nieokreślonym.

Wybór wielokrotny w widoku drzewa

Wybrane węzły są dodawane do kolekcji SelectedNodes widoku drzewa. Możesz wywołać metodę SelectAll , aby wybrać wszystkie węzły w widoku drzewa.

Uwaga / Notatka

Jeśli wywołasz metodę SelectAll, wszystkie zrealizowane węzły zostaną wybrane niezależnie od opcji SelectionMode. Aby zapewnić spójne środowisko użytkownika, należy wywołać metodę SelectAll tylko wtedy, gdy SelectionMode jest ustawiony na Multiple.

Wybór i węzły realizowane/nierealizowane

Jeśli widok drzewa ma niezrealizowane węzły, nie są brane pod uwagę przy wyborze. Poniżej przedstawiono kilka kwestii, o których należy pamiętać, jeśli chodzi o wybór z niezrealizowanymi węzłami.

  • Jeśli użytkownik wybierze węzeł nadrzędny, zostaną również wybrane wszystkie zrealizowane elementy podrzędne w ramach tego elementu nadrzędnego. Podobnie, jeżeli wszystkie węzły podrzędne są wybrane, węzeł nadrzędny także zostaje wybrany.
  • Metoda SelectAll dodaje do kolekcji SelectedNodes tylko zrealizowane węzły.
  • Jeśli wybrano węzeł nadrzędny z niezrealizowanymi elementami podrzędnymi, zostaną wybrane elementy podrzędne w miarę ich realizacji.

WybranaPozycja/WybranePozycje

Kontrolka TreeView ma właściwości SelectedItem i SelectedItems. Możesz użyć tych właściwości, aby bezpośrednio pobrać zawartość wybranych węzłów. Jeśli włączono wiele zaznaczeń, selectedItem zawiera pierwszy element w kolekcji SelectedItems.

ZmianaWyboru

Możesz obsłużyć zdarzenie SelectionChanged , aby reagować, gdy kolekcja wybranych elementów zmienia się programowo lub za pośrednictwem interakcji użytkownika.

<TreeView ItemsSource="{x:Bind Folders}"
          SelectionMode="Multiple"
          SelectionChanged="TreeView_SelectionChanged"/>
public void TreeView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args)
{
    foreach (object item in args.RemovedItems)
    {
        this.SelectedFolders.Remove((Folder)item);
    }

    foreach (object item in args.AddedItems)
    {
        this.SelectedFolders.Add((Folder)item);
    }
}

Przykłady kodu

W poniższych przykładach kodu przedstawiono różne funkcje kontrolki widoku drzewa.

Widok drzewa przy użyciu języka XAML

W tym przykładzie pokazano, jak utworzyć prostą strukturę widoku drzewa w języku XAML. Widok drzewa przedstawia smaki lodów i dodatki, które użytkownik może wybrać, rozmieszczone w kategoriach. Wybór wielokrotny jest włączony, a gdy użytkownik kliknie przycisk, wybrane elementy są wyświetlane w interfejsie użytkownika aplikacji głównej.

<Page
    x:Class="TreeViewTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          Padding="100">
        <SplitView IsPaneOpen="True"
               DisplayMode="Inline"
               OpenPaneLength="296">
            <SplitView.Pane>
                <muxc:TreeView x:Name="DessertTree" SelectionMode="Multiple">
                    <muxc:TreeView.RootNodes>
                        <muxc:TreeViewNode Content="Flavors" IsExpanded="True">
                            <muxc:TreeViewNode.Children>
                                <muxc:TreeViewNode Content="Vanilla"/>
                                <muxc:TreeViewNode Content="Strawberry"/>
                                <muxc:TreeViewNode Content="Chocolate"/>
                            </muxc:TreeViewNode.Children>
                        </muxc:TreeViewNode>

                        <muxc:TreeViewNode Content="Toppings">
                            <muxc:TreeViewNode.Children>
                                <muxc:TreeViewNode Content="Candy">
                                    <muxc:TreeViewNode.Children>
                                        <muxc:TreeViewNode Content="Chocolate"/>
                                        <muxc:TreeViewNode Content="Mint"/>
                                        <muxc:TreeViewNode Content="Sprinkles"/>
                                    </muxc:TreeViewNode.Children>
                                </muxc:TreeViewNode>
                                <muxc:TreeViewNode Content="Fruits">
                                    <muxc:TreeViewNode.Children>
                                        <muxc:TreeViewNode Content="Mango"/>
                                        <muxc:TreeViewNode Content="Peach"/>
                                        <muxc:TreeViewNode Content="Kiwi"/>
                                    </muxc:TreeViewNode.Children>
                                </muxc:TreeViewNode>
                                <muxc:TreeViewNode Content="Berries">
                                    <muxc:TreeViewNode.Children>
                                        <muxc:TreeViewNode Content="Strawberry"/>
                                        <muxc:TreeViewNode Content="Blueberry"/>
                                        <muxc:TreeViewNode Content="Blackberry"/>
                                    </muxc:TreeViewNode.Children>
                                </muxc:TreeViewNode>
                            </muxc:TreeViewNode.Children>
                        </muxc:TreeViewNode>
                    </muxc:TreeView.RootNodes>
                </muxc:TreeView>
            </SplitView.Pane>

            <StackPanel Grid.Column="1" Margin="12,0">
                <Button Content="Select all" Click="SelectAllButton_Click"/>
                <Button Content="Create order" Click="OrderButton_Click" Margin="0,12"/>
                <TextBlock Text="Your flavor selections:" Style="{StaticResource CaptionTextBlockStyle}"/>
                <TextBlock x:Name="FlavorList" Margin="0,0,0,12"/>
                <TextBlock Text="Your topping selections:" Style="{StaticResource CaptionTextBlockStyle}"/>
                <TextBlock x:Name="ToppingList"/>
            </StackPanel>
        </SplitView>
    </Grid>
</Page>
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using muxc = Microsoft.UI.Xaml.Controls;

namespace TreeViewTest
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void OrderButton_Click(object sender, RoutedEventArgs e)
        {
            FlavorList.Text = string.Empty;
            ToppingList.Text = string.Empty;

            foreach (muxc.TreeViewNode node in DessertTree.SelectedNodes)
            {
                if (node.Parent.Content?.ToString() == "Flavors")
                {
                    FlavorList.Text += node.Content + "; ";
                }
                else if (node.HasChildren == false)
                {
                    ToppingList.Text += node.Content + "; ";
                }
            }
        }

        private void SelectAllButton_Click(object sender, RoutedEventArgs e)
        {
            if (DessertTree.SelectionMode == muxc.TreeViewSelectionMode.Multiple)
            {
                DessertTree.SelectAll();
            }
        }
    }
}
Private Sub OrderButton_Click(sender As Object, e As RoutedEventArgs)
    FlavorList.Text = String.Empty
    ToppingList.Text = String.Empty
    For Each node As muxc.TreeViewNode In DessertTree.SelectedNodes
        If node.Parent.Content?.ToString() = "Flavors" Then
            FlavorList.Text += node.Content & "; "
        ElseIf node.HasChildren = False Then
            ToppingList.Text += node.Content & "; "
        End If
    Next
End Sub

Private Sub SelectAllButton_Click(sender As Object, e As RoutedEventArgs)
    If DessertTree.SelectionMode = muxc.TreeViewSelectionMode.Multiple Then
        DessertTree.SelectAll()
    End If
End Sub

Widok drzewa przy użyciu powiązania danych

W tym przykładzie pokazano, jak utworzyć ten sam widok drzewa co w poprzednim przykładzie. Jednak zamiast tworzyć hierarchię danych w języku XAML, dane są tworzone w kodzie i powiązane z właściwością ItemsSource widoku drzewa. (Programy obsługi zdarzeń przycisku pokazane w poprzednim przykładzie dotyczą również tego przykładu).

<Page
    x:Class="TreeViewTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
    xmlns:local="using:TreeViewTest"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          Padding="100">
        <SplitView IsPaneOpen="True"
                   DisplayMode="Inline"
                   OpenPaneLength="296">
            <SplitView.Pane>
                <muxc:TreeView Name="DessertTree"
                                      SelectionMode="Multiple"
                                      ItemsSource="{x:Bind DataSource}">
                    <muxc:TreeView.ItemTemplate>
                        <DataTemplate x:DataType="local:Item">
                            <muxc:TreeViewItem
                                ItemsSource="{x:Bind Children}"
                                Content="{x:Bind Name}"/>
                        </DataTemplate>
                    </muxc:TreeView.ItemTemplate>
                </muxc:TreeView>
            </SplitView.Pane>

            <StackPanel Grid.Column="1" Margin="12,0">
                <Button Content="Select all"
                        Click="SelectAllButton_Click"/>
                <Button Content="Create order"
                        Click="OrderButton_Click"
                        Margin="0,12"/>
                <TextBlock Text="Your flavor selections:"
                           Style="{StaticResource CaptionTextBlockStyle}"/>
                <TextBlock x:Name="FlavorList" Margin="0,0,0,12"/>
                <TextBlock Text="Your topping selections:"
                           Style="{StaticResource CaptionTextBlockStyle}"/>
                <TextBlock x:Name="ToppingList"/>
            </StackPanel>
        </SplitView>
    </Grid>

</Page>
using System.Collections.ObjectModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using muxc = Microsoft.UI.Xaml.Controls;

namespace TreeViewTest
{
    public sealed partial class MainPage : Page
    {
        private ObservableCollection<Item> DataSource = new ObservableCollection<Item>();

        public MainPage()
        {
            this.InitializeComponent();
            DataSource = GetDessertData();
        }

        private ObservableCollection<Item> GetDessertData()
        {
            var list = new ObservableCollection<Item>();

            Item flavorsCategory = new Item()
            {
                Name = "Flavors",
                Children =
                {
                    new Item() { Name = "Vanilla" },
                    new Item() { Name = "Strawberry" },
                    new Item() { Name = "Chocolate" }
                }
            };

            Item toppingsCategory = new Item()
            {
                Name = "Toppings",
                Children =
                {
                    new Item()
                    {
                        Name = "Candy",
                        Children =
                        {
                            new Item() { Name = "Chocolate" },
                            new Item() { Name = "Mint" },
                            new Item() { Name = "Sprinkles" }
                        }
                    },
                    new Item()
                    {
                        Name = "Fruits",
                        Children =
                        {
                            new Item() { Name = "Mango" },
                            new Item() { Name = "Peach" },
                            new Item() { Name = "Kiwi" }
                        }
                    },
                    new Item()
                    {
                        Name = "Berries",
                        Children =
                        {
                            new Item() { Name = "Strawberry" },
                            new Item() { Name = "Blueberry" },
                            new Item() { Name = "Blackberry" }
                        }
                    }
                }
            };

            list.Add(flavorsCategory);
            list.Add(toppingsCategory);
            return list;
        }

        private void OrderButton_Click(object sender, RoutedEventArgs e)
        {
            FlavorList.Text = string.Empty;
            ToppingList.Text = string.Empty;

            foreach (muxc.TreeViewNode node in DessertTree.SelectedNodes)
            {
                if (node.Parent.Content?.ToString() == "Flavors")
                {
                    FlavorList.Text += node.Content + "; ";
                }
                else if (node.HasChildren == false)
                {
                    ToppingList.Text += node.Content + "; ";
                }
            }
        }

        private void SelectAllButton_Click(object sender, RoutedEventArgs e)
        {
            if (DessertTree.SelectionMode == muxc.TreeViewSelectionMode.Multiple)
            {
                DessertTree.SelectAll();
            }
        }
    }

    public class Item
    {
        public string Name { get; set; }
        public ObservableCollection<Item> Children { get; set; } = new ObservableCollection<Item>();

        public override string ToString()
        {
            return Name;
        }
    }
}

Widok drzewa obrazów i biblioteki muzycznej

W tym przykładzie pokazano, jak utworzyć widok drzewa przedstawiający zawartość i strukturę bibliotek obrazów i muzyki użytkownika. Liczba elementów nie może być znana z wyprzedzeniem, więc każdy węzeł jest wypełniany po rozwinięciu, a opróżniany po zwinięciu.

Szablon elementu niestandardowego służy do wyświetlania elementów danych, które są typu IStorageItem.

Ważne

Kod w tym przykładzie wymaga uprawnień picturesLibrary i musicLibrary. Aby uzyskać więcej informacji na temat dostępu do plików, zobacz Uprawnienia dostępu do plików, Wyliczanie i wykonywanie zapytań o pliki i foldery oraz Pliki i foldery w bibliotekach Muzyka, Obrazy i Filmy wideo.

<Page
    x:Class="TreeViewTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TreeViewTest"
    xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
    xmlns:storage="using:Windows.Storage"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Page.Resources>
        <DataTemplate x:Key="TreeViewItemDataTemplate" x:DataType="muxc:TreeViewNode">
            <Grid Height="44">
                <TextBlock Text="{x:Bind ((storage:IStorageItem)Content).Name}"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Center"
                           Style="{ThemeResource BodyTextBlockStyle}"/>
            </Grid>
        </DataTemplate>

        <DataTemplate x:Key="MusicItemDataTemplate" x:DataType="muxc:TreeViewNode">
            <StackPanel Height="44" Orientation="Horizontal">
                <SymbolIcon Symbol="Audio" Margin="0,0,4,0"/>
                <TextBlock Text="{x:Bind ((storage:StorageFile)Content).DisplayName}"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Center"
                           Style="{ThemeResource BodyTextBlockStyle}"/>
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="PictureItemDataTemplate" x:DataType="muxc:TreeViewNode">
            <StackPanel Height="44" Orientation="Horizontal">
                <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xEB9F;"
                          Margin="0,0,4,0"/>
                <TextBlock Text="{x:Bind ((storage:StorageFile)Content).DisplayName}"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Center"
                           Style="{ThemeResource BodyTextBlockStyle}"/>
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="MusicFolderDataTemplate" x:DataType="muxc:TreeViewNode">
            <StackPanel Height="44" Orientation="Horizontal">
                <SymbolIcon Symbol="MusicInfo" Margin="0,0,4,0"/>
                <TextBlock Text="{x:Bind ((storage:StorageFolder)Content).DisplayName}"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Center"
                           Style="{ThemeResource BodyTextBlockStyle}"/>
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="PictureFolderDataTemplate" x:DataType="muxc:TreeViewNode">
            <StackPanel Height="44" Orientation="Horizontal">
                <SymbolIcon Symbol="Pictures" Margin="0,0,4,0"/>
                <TextBlock Text="{x:Bind ((storage:StorageFolder)Content).DisplayName}"
                           HorizontalAlignment="Left"
                           VerticalAlignment="Center"
                           Style="{ThemeResource BodyTextBlockStyle}"/>
            </StackPanel>
        </DataTemplate>

        <local:ExplorerItemTemplateSelector
            x:Key="ExplorerItemTemplateSelector"
            DefaultTemplate="{StaticResource TreeViewItemDataTemplate}"
            MusicItemTemplate="{StaticResource MusicItemDataTemplate}"
            MusicFolderTemplate="{StaticResource MusicFolderDataTemplate}"
            PictureItemTemplate="{StaticResource PictureItemDataTemplate}"
            PictureFolderTemplate="{StaticResource PictureFolderDataTemplate}"/>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <SplitView IsPaneOpen="True"
                   DisplayMode="Inline"
                   OpenPaneLength="296">
            <SplitView.Pane>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Button Content="Refresh tree" Click="RefreshButton_Click" Margin="24,12"/>
                    <muxc:TreeView x:Name="sampleTreeView" Grid.Row="1" SelectionMode="Single"
                              ItemTemplateSelector="{StaticResource ExplorerItemTemplateSelector}"
                              Expanding="SampleTreeView_Expanding"
                              Collapsed="SampleTreeView_Collapsed"
                              ItemInvoked="SampleTreeView_ItemInvoked"/>
                </Grid>
            </SplitView.Pane>

            <StackPanel Grid.Column="1" Margin="12,72">
                <TextBlock Text="File name:" Style="{StaticResource CaptionTextBlockStyle}"/>
                <TextBlock x:Name="FileNameTextBlock" Margin="0,0,0,12"/>

                <TextBlock Text="File path:" Style="{StaticResource CaptionTextBlockStyle}"/>
                <TextBlock x:Name="FilePathTextBlock" Margin="0,0,0,12"/>

                <TextBlock Text="Tree depth:" Style="{StaticResource CaptionTextBlockStyle}"/>
                <TextBlock x:Name="TreeDepthTextBlock" Margin="0,0,0,12"/>
            </StackPanel>
        </SplitView>
    </Grid>
</Page>
using System;
using System.Collections.Generic;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using muxc = Microsoft.UI.Xaml.Controls;

namespace TreeViewTest
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            InitializeTreeView();
        }

        private void InitializeTreeView()
        {
            // A TreeView can have more than 1 root node. The Pictures library
            // and the Music library will each be a root node in the tree.
            // Get Pictures library.
            StorageFolder picturesFolder = KnownFolders.PicturesLibrary;
            muxc.TreeViewNode pictureNode = new muxc.TreeViewNode();
            pictureNode.Content = picturesFolder;
            pictureNode.IsExpanded = true;
            pictureNode.HasUnrealizedChildren = true;
            sampleTreeView.RootNodes.Add(pictureNode);
            FillTreeNode(pictureNode);

            // Get Music library.
            StorageFolder musicFolder = KnownFolders.MusicLibrary;
            muxc.TreeViewNode musicNode = new muxc.TreeViewNode();
            musicNode.Content = musicFolder;
            musicNode.IsExpanded = true;
            musicNode.HasUnrealizedChildren = true;
            sampleTreeView.RootNodes.Add(musicNode);
            FillTreeNode(musicNode);
        }

        private async void FillTreeNode(muxc.TreeViewNode node)
        {
            // Get the contents of the folder represented by the current tree node.
            // Add each item as a new child node of the node that's being expanded.

            // Only process the node if it's a folder and has unrealized children.
            StorageFolder folder = null;

            if (node.Content is StorageFolder && node.HasUnrealizedChildren == true)
            {
                folder = node.Content as StorageFolder;
            }
            else
            {
                // The node isn't a folder, or it's already been filled.
                return;
            }

            IReadOnlyList<IStorageItem> itemsList = await folder.GetItemsAsync();

            if (itemsList.Count == 0)
            {
                // The item is a folder, but it's empty. Leave HasUnrealizedChildren = true so
                // that the chevron appears, but don't try to process children that aren't there.
                return;
            }

            foreach (var item in itemsList)
            {
                var newNode = new muxc.TreeViewNode();
                newNode.Content = item;

                if (item is StorageFolder)
                {
                    // If the item is a folder, set HasUnrealizedChildren to true.
                    // This makes the collapsed chevron show up.
                    newNode.HasUnrealizedChildren = true;
                }
                else
                {
                    // Item is StorageFile. No processing needed for this scenario.
                }

                node.Children.Add(newNode);
            }

            // Children were just added to this node, so set HasUnrealizedChildren to false.
            node.HasUnrealizedChildren = false;
        }

        private void SampleTreeView_Expanding(muxc.TreeView sender, muxc.TreeViewExpandingEventArgs args)
        {
            if (args.Node.HasUnrealizedChildren)
            {
                FillTreeNode(args.Node);
            }
        }

        private void SampleTreeView_Collapsed(muxc.TreeView sender, muxc.TreeViewCollapsedEventArgs args)
        {
            args.Node.Children.Clear();
            args.Node.HasUnrealizedChildren = true;
        }

        private void SampleTreeView_ItemInvoked(muxc.TreeView sender, muxc.TreeViewItemInvokedEventArgs args)
        {
            var node = args.InvokedItem as muxc.TreeViewNode;

            if (node.Content is IStorageItem item)
            {
                FileNameTextBlock.Text = item.Name;
                FilePathTextBlock.Text = item.Path;
                TreeDepthTextBlock.Text = node.Depth.ToString();

                if (node.Content is StorageFolder)
                {
                    node.IsExpanded = !node.IsExpanded;
                }
            }
        }

        private void RefreshButton_Click(object sender, RoutedEventArgs e)
        {
            sampleTreeView.RootNodes.Clear();
            InitializeTreeView();
        }
    }

    public class ExplorerItemTemplateSelector : DataTemplateSelector
    {
        public DataTemplate DefaultTemplate { get; set; }
        public DataTemplate MusicItemTemplate { get; set; }
        public DataTemplate PictureItemTemplate { get; set; }
        public DataTemplate MusicFolderTemplate { get; set; }
        public DataTemplate PictureFolderTemplate { get; set; }

        protected override DataTemplate SelectTemplateCore(object item)
        {
            var node = (muxc.TreeViewNode)item;

            if (node.Content is StorageFolder)
            {
                var content = node.Content as StorageFolder;
                if (content.DisplayName.StartsWith("Pictures")) return PictureFolderTemplate;
                if (content.DisplayName.StartsWith("Music")) return MusicFolderTemplate;
            }
            else if (node.Content is StorageFile)
            {
                var content = node.Content as StorageFile;
                if (content.ContentType.StartsWith("image")) return PictureItemTemplate;
                if (content.ContentType.StartsWith("audio")) return MusicItemTemplate;

            }
            return DefaultTemplate;
        }
    }
}
Public NotInheritable Class MainPage
    Inherits Page

    Public Sub New()
        InitializeComponent()
        InitializeTreeView()
    End Sub

    Private Sub InitializeTreeView()
        ' A TreeView can have more than 1 root node. The Pictures library
        ' and the Music library will each be a root node in the tree.
        ' Get Pictures library.
        Dim picturesFolder As StorageFolder = KnownFolders.PicturesLibrary
        Dim pictureNode As New muxc.TreeViewNode With {
        .Content = picturesFolder,
        .IsExpanded = True,
        .HasUnrealizedChildren = True
    }
        sampleTreeView.RootNodes.Add(pictureNode)
        FillTreeNode(pictureNode)

        ' Get Music library.
        Dim musicFolder As StorageFolder = KnownFolders.MusicLibrary
        Dim musicNode As New muxc.TreeViewNode With {
        .Content = musicFolder,
        .IsExpanded = True,
        .HasUnrealizedChildren = True
    }
        sampleTreeView.RootNodes.Add(musicNode)
        FillTreeNode(musicNode)
    End Sub

    Private Async Sub FillTreeNode(node As muxc.TreeViewNode)
        ' Get the contents of the folder represented by the current tree node.
        ' Add each item as a new child node of the node that's being expanded.

        ' Only process the node if it's a folder and has unrealized children.
        Dim folder As StorageFolder = Nothing
        If TypeOf node.Content Is StorageFolder AndAlso node.HasUnrealizedChildren Then
            folder = TryCast(node.Content, StorageFolder)
        Else
            ' The node isn't a folder, or it's already been filled.
            Return
        End If

        Dim itemsList As IReadOnlyList(Of IStorageItem) = Await folder.GetItemsAsync()
        If itemsList.Count = 0 Then
            ' The item is a folder, but it's empty. Leave HasUnrealizedChildren = true so
            ' that the chevron appears, but don't try to process children that aren't there.
            Return
        End If

        For Each item In itemsList
            Dim newNode As New muxc.TreeViewNode With {
            .Content = item
        }
            If TypeOf item Is StorageFolder Then
                ' If the item is a folder, set HasUnrealizedChildren to True.
                ' This makes the collapsed chevron show up.
                newNode.HasUnrealizedChildren = True
            Else
                ' Item is StorageFile. No processing needed for this scenario.
            End If
            node.Children.Add(newNode)
        Next

        ' Children were just added to this node, so set HasUnrealizedChildren to False.
        node.HasUnrealizedChildren = False
    End Sub

    Private Sub SampleTreeView_Expanding(sender As muxc.TreeView, args As muxc.TreeViewExpandingEventArgs)
        If args.Node.HasUnrealizedChildren Then
            FillTreeNode(args.Node)
        End If
    End Sub

    Private Sub SampleTreeView_Collapsed(sender As muxc.TreeView, args As muxc.TreeViewCollapsedEventArgs)
        args.Node.Children.Clear()
        args.Node.HasUnrealizedChildren = True
    End Sub

    Private Sub SampleTreeView_ItemInvoked(sender As muxc.TreeView, args As muxc.TreeViewItemInvokedEventArgs)
        Dim node = TryCast(args.InvokedItem, muxc.TreeViewNode)
        Dim item = TryCast(node.Content, IStorageItem)
        If item IsNot Nothing Then
            FileNameTextBlock.Text = item.Name
            FilePathTextBlock.Text = item.Path
            TreeDepthTextBlock.Text = node.Depth.ToString()
            If TypeOf node.Content Is StorageFolder Then
                node.IsExpanded = Not node.IsExpanded
            End If
        End If
    End Sub

    Private Sub RefreshButton_Click(sender As Object, e As RoutedEventArgs)
        sampleTreeView.RootNodes.Clear()
        InitializeTreeView()
    End Sub

End Class

Public Class ExplorerItemTemplateSelector
    Inherits DataTemplateSelector

    Public Property DefaultTemplate As DataTemplate
    Public Property MusicItemTemplate As DataTemplate
    Public Property PictureItemTemplate As DataTemplate
    Public Property MusicFolderTemplate As DataTemplate
    Public Property PictureFolderTemplate As DataTemplate

    Protected Overrides Function SelectTemplateCore(ByVal item As Object) As DataTemplate
        Dim node = CType(item, muxc.TreeViewNode)

        If TypeOf node.Content Is StorageFolder Then
            Dim content = TryCast(node.Content, StorageFolder)
            If content.DisplayName.StartsWith("Pictures") Then Return PictureFolderTemplate
            If content.DisplayName.StartsWith("Music") Then Return MusicFolderTemplate
        ElseIf TypeOf node.Content Is StorageFile Then
            Dim content = TryCast(node.Content, StorageFile)
            If content.ContentType.StartsWith("image") Then Return PictureItemTemplate
            If content.ContentType.StartsWith("audio") Then Return MusicItemTemplate
        End If

        Return DefaultTemplate
    End Function
End Class

Przeciąganie i upuszczanie elementów między widokami drzewa

W poniższym przykładzie pokazano, jak utworzyć dwa widoki drzewa, których elementy można przeciągać i upuszczać między sobą. Gdy element zostanie przeciągnięty do innego widoku drzewa, zostanie dodany na końcu listy. Jednak elementy można porządkować ponownie w widoku drzewa. W tym przykładzie uwzględnia się tylko widoki drzewa z jednym węzłem głównym.

<Page
    x:Class="TreeViewTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <TreeView x:Name="treeView1"
                  AllowDrop="True"
                  CanDragItems="True"
                  CanReorderItems="True"
                  DragOver="TreeView_DragOver"
                  Drop="TreeView_Drop"
                  DragItemsStarting="TreeView_DragItemsStarting"
                  DragItemsCompleted="TreeView_DragItemsCompleted"/>
        <TreeView x:Name="treeView2"
                  AllowDrop="True"
                  Grid.Column="1"
                  CanDragItems="True"
                  CanReorderItems="True"
                  DragOver="TreeView_DragOver"
                  Drop="TreeView_Drop"
                  DragItemsStarting="TreeView_DragItemsStarting"
                  DragItemsCompleted="TreeView_DragItemsCompleted"/>

    </Grid>

</Page>
using System;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace TreeViewTest
{
    public sealed partial class MainPage : Page
    {
        private TreeViewNode deletedItem;
        private TreeView sourceTreeView;

        public MainPage()
        {
            this.InitializeComponent();
            InitializeTreeView();
        }

        private void InitializeTreeView()
        {
            TreeViewNode parentNode1 = new TreeViewNode() { Content = "tv1" };
            TreeViewNode parentNode2 = new TreeViewNode() { Content = "tv2" };

            parentNode1.Children.Add(new TreeViewNode() { Content = "tv1FirstChild" });
            parentNode1.Children.Add(new TreeViewNode() { Content = "tv1SecondChild" });
            parentNode1.Children.Add(new TreeViewNode() { Content = "tv1ThirdChild" });
            parentNode1.Children.Add(new TreeViewNode() { Content = "tv1FourthChild" });
            parentNode1.IsExpanded = true;
            treeView1.RootNodes.Add(parentNode1);

            parentNode2.Children.Add(new TreeViewNode() { Content = "tv2FirstChild" });
            parentNode2.Children.Add(new TreeViewNode() { Content = "tv2SecondChild" });
            parentNode2.IsExpanded = true;
            treeView2.RootNodes.Add(parentNode2);
        }

        private void TreeView_DragOver(object sender, DragEventArgs e)
        {
            if (e.DataView.Contains(StandardDataFormats.Text))
            {
                e.AcceptedOperation = DataPackageOperation.Move;
            }
        }

        private async void TreeView_Drop(object sender, DragEventArgs e)
        {
            if (e.DataView.Contains(StandardDataFormats.Text))
            {
                string text = await e.DataView.GetTextAsync();
                TreeView destinationTreeView = sender as TreeView;

                if (destinationTreeView.RootNodes != null)
                {
                    TreeViewNode newNode = new TreeViewNode() { Content = text };
                    destinationTreeView.RootNodes[0].Children.Add(newNode);
                    deletedItem = newNode;
                }
            }
        }

        private void TreeView_DragItemsStarting(TreeView sender, TreeViewDragItemsStartingEventArgs args)
        {
            if (args.Items.Count == 1)
            {
                args.Data.RequestedOperation = DataPackageOperation.Move;
                sourceTreeView = sender;

                foreach (var item in args.Items)
                {
                    args.Data.SetText(item.ToString());
                }
            }
        }

        private void TreeView_DragItemsCompleted(TreeView sender, TreeViewDragItemsCompletedEventArgs args)
        {
            var children = sourceTreeView.RootNodes[0].Children;

            if (deletedItem != null)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    if (children[i].Content.ToString() == deletedItem.Content.ToString())
                    {
                        children.RemoveAt(i);
                        break;
                    }
                }
            }

            sourceTreeView = null;
            deletedItem = null;
        }
    }
}

UwP i WinUI 2

Ważne

Informacje i przykłady w tym artykule są zoptymalizowane pod kątem aplikacji korzystających z zestawu Windows App SDK i WinUI 3, ale są ogólnie stosowane do aplikacji platformy UWP korzystających z interfejsu WinUI 2. Zobacz dokumentację interfejsu API platformy UWP, aby uzyskać informacje i przykłady dotyczące platformy.

Ta sekcja zawiera informacje potrzebne do używania kontrolki w aplikacji platformy UWP lub WinUI 2.

Element TreeView dla aplikacji platformy UWP jest dołączany jako część interfejsu WinUI 2. Aby uzyskać więcej informacji, w tym instrukcje dotyczące instalacji, zobacz WinUI 2. Interfejsy API dla tej kontrolki istnieją zarówno w przestrzeniach nazw Windows.UI.Xaml.Controls (UWP) i Microsoft.UI.Xaml.Controls (WinUI).

Zalecamy użycie najnowszego interfejsu WinUI 2 , aby uzyskać najbardziej aktualne style, szablony i funkcje dla wszystkich kontrolek.

Aby użyć kodu z tego artykułu w WinUI 2, użyj aliasu w XAML (używamy muxc), aby reprezentować API Biblioteki Interfejsu Użytkownika Windows, które są uwzględnione w Twoim projekcie. Aby uzyskać więcej informacji, zobacz Wprowadzenie do interfejsu WinUI 2 .

xmlns:muxc="using:Microsoft.UI.Xaml.Controls"

<muxc:TreeView>
    <muxc:TreeView.RootNodes>
        <muxc:TreeViewNode Content="Flavors">
            <muxc:TreeViewNode.Children>
                <muxc:TreeViewNode Content="Vanilla"/>
            </muxc:TreeViewNode.Children>
        </muxc:TreeViewNode>
    </muxc:TreeView.RootNodes>
</muxc:TreeView>