Przegląd Szablonowanie danych

Model tworzenia szablonów danych WPF zapewnia dużą elastyczność definiowania prezentacji danych. Kontrolki WPF mają wbudowaną funkcjonalność do obsługi dostosowywania prezentacji danych. W tym temacie najpierw pokazano, jak zdefiniować element DataTemplate , a następnie wprowadzić inne funkcje tworzenia szablonów danych, takie jak wybór szablonów na podstawie logiki niestandardowej i obsługa wyświetlania danych hierarchicznych.

Wymagania wstępne

Ten temat koncentruje się na funkcjach tworzenia szablonów danych i nie jest wprowadzeniem pojęć związanych z powiązaniem danych. Aby uzyskać informacje na temat podstawowych pojęć związanych z powiązaniem danych, zobacz Omówienie powiązania danych.

DataTemplate dotyczy prezentacji danych i jest jedną z wielu funkcji oferowanych przez model stylów I szablonów WPF. Aby zapoznać się z wprowadzeniem modelu stylów I tworzenia szablonów WPF, takich jak sposób używania Style elementu do ustawiania właściwości kontrolek, zobacz temat Styling and Templating (Styling and Templating).

Ponadto ważne jest, aby zrozumieć Resources, które są zasadniczo tym, co umożliwia obiektom, takim jak Style i DataTemplate wielokrotnego użytku. Aby uzyskać więcej informacji na temat zasobów, zobacz Zasoby XAML.

Podstawy tworzenia szablonów danych

Aby zademonstrować, dlaczego DataTemplate jest to ważne, przyjrzyjmy się przykładowi powiązania danych. W tym przykładzie mamy ListBox obiekt, który jest powiązany z listą Task obiektów. Każdy Task obiekt ma TaskName (ciąg), Description (ciąg), ( Priority int) i właściwość typu TaskType, która jest elementem Enum z wartościami Home i Work.

<Window x:Class="SDKSample.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:SDKSample"
  Title="Introduction to Data Templating Sample">
  <Window.Resources>
    <local:Tasks x:Key="myTodoList"/>

</Window.Resources>
  <StackPanel>
    <TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
    <ListBox Width="400" Margin="10"
             ItemsSource="{Binding Source={StaticResource myTodoList}}"/>
  </StackPanel>
</Window>

Bez elementu DataTemplate

Bez elementu DataTemplatenasz ListBox obecnie wygląda następująco:

Data templating sample screenshot

Dzieje się tak, że bez żadnych konkretnych instrukcji ListBox domyślne wywołania ToString podczas próby wyświetlenia obiektów w kolekcji. W związku z tym, jeśli Task obiekt zastępuje metodę ToString , ListBox obiekt wyświetla reprezentację ciągu każdego obiektu źródłowego w kolekcji bazowej.

Jeśli na przykład Task klasa zastępuje metodę ToString w ten sposób, gdzie name jest polem TaskName właściwości :

public override string ToString()
{
    return name.ToString();
}
Public Overrides Function ToString() As String
    Return _name.ToString()
End Function

Następnie wygląda ListBox następująco:

Data templating sample screenshot

Jednak jest to ograniczenie i nieelastyczne. Ponadto w przypadku powiązania z danymi XML nie można zastąpić ToStringelementu .

Definiowanie prostej tablicy danych

Rozwiązaniem jest zdefiniowanie .DataTemplate Jednym ze sposobów, aby to zrobić, jest ustawienie ItemTemplate właściwości obiektu ListBox na DataTemplatewartość . To, co określisz w obiekcie DataTemplate danych, staje się wizualną strukturą obiektu danych. Poniżej DataTemplate przedstawiono dość proste. Udostępniamy instrukcje, że każdy element jest wyświetlany jako trzy TextBlock elementy w obiekcie StackPanel. Każdy TextBlock element jest powiązany z właściwością Task klasy .

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}">
   <ListBox.ItemTemplate>
     <DataTemplate>
       <StackPanel>
         <TextBlock Text="{Binding Path=TaskName}" />
         <TextBlock Text="{Binding Path=Description}"/>
         <TextBlock Text="{Binding Path=Priority}"/>
       </StackPanel>
     </DataTemplate>
   </ListBox.ItemTemplate>
 </ListBox>

Podstawowe dane przykładów w tym temacie to kolekcja obiektów CLR. Jeśli wiążesz się z danymi XML, podstawowe pojęcia są takie same, ale istnieje niewielka różnica składniowa. Na przykład zamiast , Path=TaskNamenależy ustawić @TaskName wartość XPath na (jeśli TaskName jest atrybutem węzła XML).

Teraz wyglądamy ListBox następująco:

Data templating sample screenshot

Tworzenie elementu DataTemplate jako zasobu

W powyższym przykładzie zdefiniowaliśmy DataTemplate wbudowany element . Częściej definiuje się go w sekcji zasobów, aby mógł być obiektem wielokrotnego użytku, jak w poniższym przykładzie:

<Window.Resources>
<DataTemplate x:Key="myTaskTemplate">
  <StackPanel>
    <TextBlock Text="{Binding Path=TaskName}" />
    <TextBlock Text="{Binding Path=Description}"/>
    <TextBlock Text="{Binding Path=Priority}"/>
  </StackPanel>
</DataTemplate>
</Window.Resources>

Teraz możesz użyć myTaskTemplate jako zasobu, jak w poniższym przykładzie:

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplate="{StaticResource myTaskTemplate}"/>

Ponieważ myTaskTemplate jest to zasób, można go teraz używać w innych kontrolkach, które mają właściwość, która przyjmuje DataTemplate typ. Jak pokazano powyżej, dla ItemsControl obiektów, takich jak ListBox, jest ItemTemplate to właściwość . W przypadku ContentControl obiektów jest ContentTemplate to właściwość .

Właściwość DataType

Klasa DataTemplate ma DataType właściwość, która jest bardzo podobna do TargetType właściwości Style klasy. W związku z tym zamiast określać element x:Key dla DataTemplate elementu w powyższym przykładzie, można wykonać następujące czynności:

<DataTemplate DataType="{x:Type local:Task}">
  <StackPanel>
    <TextBlock Text="{Binding Path=TaskName}" />
    <TextBlock Text="{Binding Path=Description}"/>
    <TextBlock Text="{Binding Path=Priority}"/>
  </StackPanel>
</DataTemplate>

Spowoduje to DataTemplate automatyczne zastosowanie do wszystkich Task obiektów. Należy pamiętać, że w tym przypadku x:Key parametr jest ustawiany niejawnie. W związku z tym, jeśli przypiszesz tę DataTemplate wartość, zastępujesz niejawną x:Keyx:Key i nie zostanie ona zastosowana DataTemplate automatycznie.

W przypadku powiązania ContentControl obiektu z kolekcją Task obiektów ContentControl obiekt nie jest używany automatycznie DataTemplate . Wynika to z tego, że powiązanie na obiekcie ContentControl wymaga dodatkowych informacji, aby określić, czy chcesz powiązać z całą kolekcją, czy z poszczególnymi obiektami. Jeśli śledzisz ContentControl wybór ItemsControl typu, możesz ustawić Path właściwość ContentControl powiązania na "/", aby wskazać, że interesuje Cię bieżący element. Aby zapoznać się z przykładem, zobacz Wiązanie z kolekcją i wyświetlanie informacji na podstawie zaznaczenia. W przeciwnym razie należy jawnie określić DataTemplate właściwość , ustawiając ContentTemplate właściwość .

Właściwość jest szczególnie przydatna DataType , gdy masz CompositeCollection różne typy obiektów danych. Aby zapoznać się z przykładem, zobacz Implementowanie kolekcji CompositeCollection.

Dodawanie większej liczby do elementu DataTemplate

Obecnie dane są wyświetlane z niezbędnymi informacjami, ale zdecydowanie jest miejsce na ulepszenie. Ulepszmy prezentację, dodając element Border, , Gridi niektóre TextBlock elementy opisujące wyświetlane dane.


<DataTemplate x:Key="myTaskTemplate">
  <Border Name="border" BorderBrush="Aqua" BorderThickness="1"
          Padding="5" Margin="5">
    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
      </Grid.ColumnDefinitions>
      <TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
      <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
      <TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
      <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
      <TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
      <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
    </Grid>
  </Border>
</DataTemplate>

Poniższy zrzut ekranu przedstawia element ListBox z tym zmodyfikowanym DataTemplateelementem :

Data templating sample screenshot

Możemy ustawić HorizontalContentAlignmentStretch wartość na na , ListBox aby upewnić się, że szerokość elementów zajmuje całe miejsce:

<ListBox Width="400" Margin="10"
     ItemsSource="{Binding Source={StaticResource myTodoList}}"
     ItemTemplate="{StaticResource myTaskTemplate}" 
     HorizontalContentAlignment="Stretch"/>

Po ustawieniu HorizontalContentAlignment właściwości na Stretchwartość ListBox element teraz wygląda następująco:

Data templating sample screenshot

Stosowanie wartości właściwości za pomocą funkcji DataTriggers

Bieżąca prezentacja nie informuje nas, czy jest Task to zadanie domowe, czy zadanie biurowe. Pamiętaj, że Task obiekt ma TaskType właściwość typu TaskType, która jest wyliczeniem z wartościami Home i Work.

W poniższym przykładzie zestawy DataTrigger elementu BorderBrush o nazwie border na Yellow , jeśli TaskType właściwość ma wartość TaskType.Home.

<DataTemplate x:Key="myTaskTemplate">
<DataTemplate.Triggers>
  <DataTrigger Binding="{Binding Path=TaskType}">
    <DataTrigger.Value>
      <local:TaskType>Home</local:TaskType>
    </DataTrigger.Value>
    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
  </DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

Nasza aplikacja wygląda teraz następująco. Zadania domowe pojawiają się z żółtym obramowaniem, a zadania biurowe pojawiają się z obramowaniem wodnym:

Data templating sample screenshot

W tym przykładzie użyto DataTrigger obiektu , Setter aby ustawić wartość właściwości. Klasy wyzwalacza mają EnterActions również właściwości i ExitActions , które umożliwiają uruchamianie zestawu akcji, takich jak animacje. Ponadto istnieje również MultiDataTrigger klasa, która umożliwia stosowanie zmian na podstawie wielu wartości właściwości powiązanych z danymi.

Alternatywnym sposobem osiągnięcia tego samego efektu jest powiązanie BorderBrush właściwości z właściwością TaskType i użycie konwertera wartości w celu zwrócenia koloru na TaskType podstawie wartości. Tworzenie powyższego efektu przy użyciu konwertera jest nieco bardziej wydajne pod względem wydajności. Ponadto tworzenie własnego konwertera zapewnia większą elastyczność, ponieważ dostarczasz własną logikę. Ostatecznie wybrana technika zależy od scenariusza i preferencji. Aby uzyskać informacje o sposobie pisania konwertera, zobacz IValueConverter.

Co należy do dataTemplate?

W poprzednim przykładzie umieściliśmy wyzwalacz w obiekcie DataTemplate przy użyciu DataTemplate.Triggers właściwości . Wyzwalacz Setter ustawia wartość właściwości elementu ( Border elementu), który znajduje się w obiekcie DataTemplate. Jeśli jednak właściwości, które dotyczą, Setters nie są właściwościami elementów, które znajdują się w bieżącym DataTemplateelemencie , może być bardziej odpowiednie do ustawienia właściwości przy użyciu klasy Style , która jest przeznaczona dla ListBoxItem klasy (jeśli kontrolka, którą tworzysz powiązanie, to ListBox). Jeśli na przykład chcesz Trigger animować Opacity wartość elementu, gdy wskaźnik myszy wskazuje element, zdefiniujesz wyzwalacze w ListBoxItem stylu. Przykład można znaleźć w przykładzie Wprowadzenie do stylów i tworzenia szablonów.

Ogólnie rzecz biorąc, należy pamiętać, że DataTemplate element jest stosowany do każdego wygenerowanego ListBoxItem elementu (aby uzyskać więcej informacji na temat sposobu i miejsca ich stosowania, zobacz ItemTemplate stronę). Zajmujesz DataTemplate się tylko prezentacją i wyglądem obiektów danych. W większości przypadków wszystkie inne aspekty prezentacji, takie jak to, jak wygląda element po jego wybraniu lub sposób ListBox definiowania elementów, nie należą do definicji DataTemplateelementu . Aby zapoznać się z przykładem, zobacz sekcję Styling and Templating an ItemsControl (Tworzenie stylów i tworzenie szablonów kontrolek elementów ).

Wybieranie elementu DataTemplate na podstawie właściwości obiektu danych

W sekcji Właściwość DataType omówiliśmy, że można zdefiniować różne szablony danych dla różnych obiektów danych. Jest to szczególnie przydatne, gdy masz CompositeCollection różne typy lub kolekcje z elementami różnych typów. W sekcji Use DataTriggers to Apply Property Values (Stosowanie wartości właściwości) pokazano, że jeśli masz kolekcję obiektów danych tego samego typu, możesz utworzyć DataTemplate element , a następnie użyć wyzwalaczy, aby zastosować zmiany na podstawie wartości właściwości każdego obiektu danych. Jednak wyzwalacze umożliwiają stosowanie wartości właściwości lub animacji początkowej, ale nie zapewniają one elastyczności w celu odtworzenia struktury obiektów danych. Niektóre scenariusze mogą wymagać utworzenia innego DataTemplate obiektu danych, które są tego samego typu, ale mają różne właściwości.

Na przykład gdy Task obiekt ma Priority wartość 1, możesz chcieć nadać mu zupełnie inny wygląd, aby służyć jako alert dla siebie. W takim przypadku utworzysz obiekt DataTemplate dla wyświetlania obiektów o wysokim priorytcie Task . Dodajmy następujące elementy DataTemplate do sekcji zasobów:

<DataTemplate x:Key="importantTaskTemplate">
  <DataTemplate.Resources>
    <Style TargetType="TextBlock">
      <Setter Property="FontSize" Value="20"/>
    </Style>
  </DataTemplate.Resources>
  <Border Name="border" BorderBrush="Red" BorderThickness="1"
          Padding="5" Margin="5">
    <DockPanel HorizontalAlignment="Center">
      <TextBlock Text="{Binding Path=Description}" />
      <TextBlock>!</TextBlock>
    </DockPanel>
  </Border>
</DataTemplate>

W tym przykładzie użyto właściwości DataTemplate.Resources . Zasoby zdefiniowane w tej sekcji są współużytkowane przez elementy w obiekcie DataTemplate.

Aby podać logikę, która DataTemplate ma być używana na Priority podstawie wartości obiektu danych, utwórz podklasę DataTemplateSelector i przesłoń metodę SelectTemplate . W poniższym przykładzie SelectTemplate metoda udostępnia logikę umożliwiającą zwrócenie odpowiedniego szablonu na podstawie wartości Priority właściwości. Szablon do zwrócenia znajduje się w zasobach elementu otaczania Window .

using System.Windows;
using System.Windows.Controls;

namespace SDKSample
{
    public class TaskListDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate
            SelectTemplate(object item, DependencyObject container)
        {
            FrameworkElement element = container as FrameworkElement;

            if (element != null && item != null && item is Task)
            {
                Task taskitem = item as Task;

                if (taskitem.Priority == 1)
                    return
                        element.FindResource("importantTaskTemplate") as DataTemplate;
                else
                    return
                        element.FindResource("myTaskTemplate") as DataTemplate;
            }

            return null;
        }
    }
}

Namespace SDKSample
    Public Class TaskListDataTemplateSelector
        Inherits DataTemplateSelector
        Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate

            Dim element As FrameworkElement
            element = TryCast(container, FrameworkElement)

            If element IsNot Nothing AndAlso item IsNot Nothing AndAlso TypeOf item Is Task Then

                Dim taskitem As Task = TryCast(item, Task)

                If taskitem.Priority = 1 Then
                    Return TryCast(element.FindResource("importantTaskTemplate"), DataTemplate)
                Else
                    Return TryCast(element.FindResource("myTaskTemplate"), DataTemplate)
                End If
            End If

            Return Nothing
        End Function
    End Class
End Namespace

Następnie możemy zadeklarować jako TaskListDataTemplateSelector zasób:

<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>

Aby użyć zasobu selektora szablonów, przypisz go do ItemTemplateSelector właściwości ListBox. Obiekt ListBox wywołuje metodę SelectTemplateTaskListDataTemplateSelector dla każdej z elementów w kolekcji bazowej. Wywołanie przekazuje obiekt danych jako parametr elementu. Element DataTemplate zwracany przez metodę jest następnie stosowany do tego obiektu danych.

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
         HorizontalContentAlignment="Stretch"/>

Po wybraniu ListBox selektora szablonu jest teraz wyświetlany w następujący sposób:

Data templating sample screenshot

To kończy naszą dyskusję na temat tego przykładu. Aby zapoznać się z kompletnym przykładem, zobacz Wprowadzenie do przykładu templating danych.

Stylowanie i tworzenie szablonów kontrolki ItemsControl

Mimo że nie ItemsControl jest to jedyny typ kontrolki, którego można użyć DataTemplate , jest to bardzo typowy scenariusz powiązania ItemsControl kolekcji. W sekcji What Belongs in a DataTemplate (Co należy do elementu DataTemplate ) omówiliśmy, że definicja użytkownika DataTemplate powinna dotyczyć tylko prezentacji danych. Aby wiedzieć, kiedy nie nadaje się do używania elementu DataTemplate , ważne jest, aby zrozumieć różne właściwości stylu i szablonu dostarczone przez usługę ItemsControl. Poniższy przykład został zaprojektowany w celu zilustrowania funkcji każdej z tych właściwości. W ItemsControl tym przykładzie jest powiązana z tą samą Tasks kolekcją co w poprzednim przykładzie. W celach demonstracyjnych style i szablony w tym przykładzie są zadeklarowane w tekście.

<ItemsControl Margin="10"
              ItemsSource="{Binding Source={StaticResource myTodoList}}">
  <!--The ItemsControl has no default visual appearance.
      Use the Template property to specify a ControlTemplate to define
      the appearance of an ItemsControl. The ItemsPresenter uses the specified
      ItemsPanelTemplate (see below) to layout the items. If an
      ItemsPanelTemplate is not specified, the default is used. (For ItemsControl,
      the default is an ItemsPanelTemplate that specifies a StackPanel.-->
  <ItemsControl.Template>
    <ControlTemplate TargetType="ItemsControl">
      <Border BorderBrush="Aqua" BorderThickness="1" CornerRadius="15">
        <ItemsPresenter/>
      </Border>
    </ControlTemplate>
  </ItemsControl.Template>
  <!--Use the ItemsPanel property to specify an ItemsPanelTemplate
      that defines the panel that is used to hold the generated items.
      In other words, use this property if you want to affect
      how the items are laid out.-->
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <!--Use the ItemTemplate to set a DataTemplate to define
      the visualization of the data objects. This DataTemplate
      specifies that each data object appears with the Proriity
      and TaskName on top of a silver ellipse.-->
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <DataTemplate.Resources>
        <Style TargetType="TextBlock">
          <Setter Property="FontSize" Value="18"/>
          <Setter Property="HorizontalAlignment" Value="Center"/>
        </Style>
      </DataTemplate.Resources>
      <Grid>
        <Ellipse Fill="Silver"/>
        <StackPanel>
          <TextBlock Margin="3,3,3,0"
                     Text="{Binding Path=Priority}"/>
          <TextBlock Margin="3,0,3,7"
                     Text="{Binding Path=TaskName}"/>
        </StackPanel>
      </Grid>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
  <!--Use the ItemContainerStyle property to specify the appearance
      of the element that contains the data. This ItemContainerStyle
      gives each item container a margin and a width. There is also
      a trigger that sets a tooltip that shows the description of
      the data object when the mouse hovers over the item container.-->
  <ItemsControl.ItemContainerStyle>
    <Style>
      <Setter Property="Control.Width" Value="100"/>
      <Setter Property="Control.Margin" Value="5"/>
      <Style.Triggers>
        <Trigger Property="Control.IsMouseOver" Value="True">
          <Setter Property="Control.ToolTip"
                  Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                          Path=Content.Description}"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </ItemsControl.ItemContainerStyle>
</ItemsControl>

Poniżej przedstawiono zrzut ekranu przedstawiający przykład renderowania:

ItemsControl example screenshot

Należy pamiętać, że zamiast używać elementu ItemTemplate, można użyć elementu ItemTemplateSelector. Zapoznaj się z poprzednią sekcją, aby zapoznać się z przykładem. Podobnie, zamiast używać ItemContainerStyleelementu , możesz użyć polecenia ItemContainerStyleSelector.

Istnieją dwie inne właściwości ItemsControl związane z stylem, które nie są tutaj wyświetlane, i GroupStyleGroupStyleSelector.

Obsługa danych hierarchicznych

Do tej pory przyjrzeliśmy się tylko temu, jak powiązać i wyświetlić pojedynczą kolekcję. Czasami masz kolekcję zawierającą inne kolekcje. Klasa jest przeznaczona HierarchicalDataTemplate do użycia z typami HeaderedItemsControl do wyświetlania takich danych. W poniższym przykładzie ListLeagueList znajduje się lista League obiektów. Każdy League obiekt ma Name kolekcję Division obiektów i. Każda z nich Division ma obiekt i kolekcję Team obiektów, a każdy Team obiekt ma obiekt NameName .

<Window x:Class="SDKSample.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="HierarchicalDataTemplate Sample"
  xmlns:src="clr-namespace:SDKSample">
  <DockPanel>
    <DockPanel.Resources>
      <src:ListLeagueList x:Key="MyList"/>

      <HierarchicalDataTemplate DataType    = "{x:Type src:League}"
                                ItemsSource = "{Binding Path=Divisions}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>

      <HierarchicalDataTemplate DataType    = "{x:Type src:Division}"
                                ItemsSource = "{Binding Path=Teams}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>

      <DataTemplate DataType="{x:Type src:Team}">
        <TextBlock Text="{Binding Path=Name}"/>
      </DataTemplate>
    </DockPanel.Resources>

    <Menu Name="menu1" DockPanel.Dock="Top" Margin="10,10,10,10">
        <MenuItem Header="My Soccer Leagues"
                  ItemsSource="{Binding Source={StaticResource MyList}}" />
    </Menu>

    <TreeView>
      <TreeViewItem ItemsSource="{Binding Source={StaticResource MyList}}" Header="My Soccer Leagues" />
    </TreeView>

  </DockPanel>
</Window>

W przykładzie pokazano, że przy użyciu programu HierarchicalDataTemplatemożna łatwo wyświetlać dane listy zawierające inne listy. Poniżej przedstawiono zrzut ekranu przedstawiający przykład.

HierarchicalDataTemplate sample screenshot

Zobacz też