Přehled datových šablon

Model šablon dat WPF poskytuje velkou flexibilitu při definování prezentace dat. Ovládací prvky WPF mají integrované funkce, které podporují přizpůsobení prezentace dat. Toto téma nejprve ukazuje, jak definovat DataTemplate a pak zavést další funkce šablon dat, jako je výběr šablon na základě vlastní logiky a podpora zobrazení hierarchických dat.

Předpoklady

Toto téma se zaměřuje na funkce šablonování dat a nejedná se o zavedení konceptů datových vazeb. Informace o základních konceptech datových vazeb najdete v přehledu datových vazeb.

DataTemplate je o prezentaci dat a je jednou z mnoha funkcí, které poskytuje model stylů a šablon WPF. Úvod do modelu stylů a šablon WPF, například jak použít Style k nastavení vlastností u ovládacích prvků, najdete v tématu Styling a Šablonování .

Kromě toho je důležité pochopit Resources, které jsou v podstatě to, co umožňuje objekty, jako Style a DataTemplate být opakovaně použitelné. Další informace o prostředcích najdete v tématu Prostředky XAML.

Základy šablon dat

Abychom si ukázali, proč DataTemplate je důležité, projdeme si příklad datové vazby. V tomto příkladu máme svázanou ListBox se seznamem Task objektů. Každý Task objekt má TaskName (řetězec), (řetězec), Description ( Priority int) a vlastnost typu TaskType, která je s Enum hodnotami Home a 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 objektu DataTemplate

DataTemplateBez nějakého , naše ListBox aktuálně vypadá takto:

Screenshot of the Introduction to Data Templating Sample window showing the My Task List ListBox displaying the string representation SDKSample.Task for each source object.

Co se děje, je to, ListBox že při pokusu o zobrazení objektů v kolekci se ve výchozím nastavení bez konkrétních pokynů ve výchozím nastavení volá ToString . Proto pokud Task objekt přepíše metodu ToString , ListBox zobrazí řetězcovou reprezentaci každého zdrojového objektu v podkladové kolekci.

Pokud například Task třída přepíše metodu ToString tímto způsobem, kde name je pole pro TaskName vlastnost:

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

ListBox Pak vypadá takto:

Screenshot of the Introduction to Data Templating Sample window showing the My Task List ListBox displaying a list of tasks.

To ale omezuje a nepružné. Pokud také vytváříte vazbu na data XML, nebudete moct přepsat ToString.

Definování jednoduchého objektu DataTemplate

Řešením je definovat .DataTemplate Jedním ze způsobů, jak to udělat, je nastavit ItemTemplate vlastnost ListBox na hodnotu DataTemplate. Tím, co zadáte ve svém DataTemplate objektu, se stane vizuální strukturou datového objektu. Toto DataTemplate je poměrně jednoduché. Dáváme instrukce, že každá položka se zobrazí jako tři TextBlock prvky v rámci .StackPanel Každý TextBlock prvek je vázán na vlastnost Task třídy.

<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>

Podkladová data pro příklady v tomto tématu jsou kolekce objektů CLR. Pokud vytváříte vazbu na data XML, základní koncepty jsou stejné, ale existuje mírný syntaktický rozdíl. Například místo toho, abyste ho Path=TaskNamenastavili XPath@TaskName (pokud TaskName je atributem uzlu XML).

Teď vypadá takto ListBox :

Screenshot of the Introduction to Data Templating Sample window showing the My Task List ListBox displaying the tasks as TextBlock elements.

Vytvoření objektu DataTemplate jako prostředku

V předchozím příkladu jsme definovali vložený DataTemplate objekt. Je častější definovat ho v oddílu prostředků, aby mohl být opakovaně použitelným objektem, jak je znázorněno v následujícím příkladu:

<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>

Nyní můžete použít myTaskTemplate jako prostředek, jak je znázorněno v následujícím příkladu:

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

Protože myTaskTemplate je prostředek, můžete ho nyní použít u jiných ovládacích prvků, které mají vlastnost, která přebírá DataTemplate typ. Jak je znázorněno výše, pro ItemsControl objekty, jako ListBoxje například , je ItemTemplate to vlastnost. U ContentControl objektů je ContentTemplate to vlastnost.

Vlastnost DataType

Třída DataTemplateDataType vlastnost, která je velmi podobná TargetType vlastnosti Style třídy. Proto místo zadání x:Key pro výše DataTemplate uvedený příklad můžete udělat následující:

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

Použije se DataTemplate automaticky pro všechny Task objekty. Všimněte si, že v tomto případě x:Key je nastaven implicitně. Proto pokud tuto DataTemplatex:Key hodnotu přiřadíte, přepíšete implicitní x:Key hodnotu a DataTemplate nebude použita automaticky.

Pokud vytváříte vazbu ContentControl na kolekci Task objektů, ContentControl výše uvedené DataTemplate hodnoty se nepoužívají automaticky. Důvodem je to, že vazba na ContentControl základě potřeby potřebuje více informací k rozlišení, zda chcete vytvořit vazbu na celou kolekci nebo jednotlivé objekty. Pokud sledujete ContentControl výběr typuItemsControl, můžete vlastnost ContentControl vazby nastavit Path na "/", která označuje, že vás zajímá aktuální položka. Příklad najdete v tématu Vytvoření vazby ke kolekci a zobrazení informací na základě výběru. V opačném případě je nutné explicitně zadat DataTemplate nastavením ContentTemplate vlastnosti.

Vlastnost DataType je obzvláště užitečná, pokud máte CompositeCollection různé typy datových objektů. Příklad naleznete v tématu Implementace CompositeCollection.

Přidání dalších dat do dataTemplate

V současné době se data zobrazují s potřebnými informacemi, ale rozhodně je zde prostor pro zlepšení. Pojďme prezentaci vylepšit přidáním symbolu Border, a Grida některých TextBlock prvků, které popisují zobrazená data.


<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>

Následující snímek obrazovky ukazuje ListBox tento upravený DataTemplatesnímek obrazovky:

Screenshot of the Introduction to Data Templating Sample window showing the My Task List ListBox with the modified DataTemplate.

Můžeme nastavit HorizontalContentAlignment na Stretch šířku ListBox položek, abychom zajistili, že šířka položek zabírá celé místo:

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

Když je vlastnost nastavená HorizontalContentAlignment na Stretch, ListBox vypadá teď takto:

Screenshot of the Introduction to Data Templating Sample window showing the My Task List ListBox stretched to fit the screen horizontally.

Použití datovýchtriggerů k použití hodnot vlastností

Aktuální prezentace nám neřekne, jestli Task je úkol doma nebo úkol kanceláře. Nezapomeňte, že Task objekt má TaskType vlastnost typu TaskType, což je výčet s hodnotami Home a Work.

V následujícím příkladu nastaví element pojmenovaný border na Yellow if the TaskType property is TaskType.Home.BorderBrushDataTrigger

<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>

Naše aplikace teď vypadá takto. Domovské úkoly se zobrazí se žlutým ohraničením a úkoly kanceláře s ohraničením aqua:

Screenshot of the Introduction to Data Templating Sample window showing the My Task List ListBox with the home and office task borders highlighted in color.

V tomto příkladu DataTrigger používá Setter k nastavení hodnoty vlastnosti. Třídy triggeru EnterActions mají také vlastnosti, ExitActions které umožňují spustit sadu akcí, jako jsou animace. Kromě toho existuje také MultiDataTrigger třída, která umožňuje použít změny na základě více hodnot vlastností vázané na data.

Alternativním způsobem, jak dosáhnout stejného efektu, je vytvořit vazbu BorderBrush vlastnosti na TaskType vlastnost a pomocí převaděče hodnot vrátit barvu na TaskType základě hodnoty. Vytvoření výše uvedeného efektu pomocí převaděče je mírně efektivnější z hlediska výkonu. Vytváření vlastního převaděče navíc poskytuje větší flexibilitu, protože dodáváte vlastní logiku. To, kterou techniku zvolíte, nakonec závisí na vašem scénáři a vašich preferencích. Informace o zápisu převaděče naleznete v tématu IValueConverter.

Co patří do objektu DataTemplate?

V předchozím příkladu jsme trigger umístili do objektu DataTemplate using vlastnost DataTemplate.Triggers . Aktivační Setter událost nastaví hodnotu vlastnosti elementu ( Border elementu), který je uvnitř DataTemplate. Pokud však vlastnosti, které se týkají vás Setters , nejsou vlastnostmi prvků, které jsou v aktuálním DataTemplatestavu , může být vhodnější nastavit vlastnosti pomocí Style vlastnosti, která je určena pro ListBoxItem třídu (pokud je ovládací prvek, který vytváříte vazby ListBoxje ). Pokud chcete Trigger například animovat Opacity hodnotu položky, když myš odkazuje na položku, definujete triggery v rámci ListBoxItem stylu. Příklad najdete v ukázce Úvod do stylů a šablon.

Obecně mějte na paměti, že DataTemplate se používá u každého vygenerovaného ListBoxItem souboru (další informace o tom, jak a kde se skutečně používá, najdete na ItemTemplate stránce.) Vaše DataTemplate záležitost se týká pouze prezentace a vzhledu datových objektů. Ve většině případů všechny ostatní aspekty prezentace, například to, jak položka vypadá, když je vybrána nebo jak ListBox obsahuje položky, nepatří do definice objektu DataTemplate. Příklad najdete v části Styling a Templating oddíl ItemsControl .

Výběr objektu DataTemplate na základě vlastností datového objektu

V části DataType Property jsme probrali, že můžete definovat různé šablony dat pro různé datové objekty. To je zvlášť užitečné, pokud máte různé CompositeCollection typy nebo kolekce s položkami různých typů. V části Použít dataTriggery k použití hodnot vlastností jsme ukázali, že pokud máte kolekci stejného typu datových objektů, můžete vytvořit DataTemplate a pak pomocí triggerů použít změny na základě hodnot vlastností každého datového objektu. Triggery ale umožňují použít hodnoty vlastností nebo počáteční animace, ale neumožňují flexibilitu při rekonstrukci struktury datových objektů. Některé scénáře můžou vyžadovat, abyste pro datové objekty, které jsou stejného typu, vytvořili jiný DataTemplate typ, ale mají různé vlastnosti.

Pokud má například Task objekt Priority hodnotu 1, můžete mu dát úplně jiný vzhled, který bude sloužit jako výstraha pro sebe. V takovém případě vytvoříte DataTemplate zobrazení objektů s vysokou prioritou Task . Pojďme do oddílu prostředků přidat následující DataTemplate položky:

<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>

Tento příklad používá DataTemplate.Resources vlastnost. Prostředky definované v tomto oddílu jsou sdíleny elementy v rámci .DataTemplate

Pokud chcete zadat logiku, která DataTemplate se má použít na Priority základě hodnoty datového objektu, vytvořte podtřídu DataTemplateSelector a přepište metodu SelectTemplate . V následujícím příkladu SelectTemplate metoda poskytuje logiku pro vrácení příslušné šablony na základě hodnoty Priority vlastnosti. Šablona, která se má vrátit, se nachází v prostředcích zkosení Window elementu.

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

Pak můžeme deklarovat TaskListDataTemplateSelector jako prostředek:

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

Chcete-li použít prostředek selektoru šablony, přiřaďte ho k ItemTemplateSelector vlastnosti objektu ListBox. Volání ListBoxSelectTemplate metody TaskListDataTemplateSelector pro každou z položek v podkladové kolekci. Volání předá datový objekt jako parametr položky. Ten DataTemplate , který je vrácen metodou, se pak použije na tento datový objekt.

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

Selektor šablony se teď ListBox zobrazí takto:

Screenshot of Introduction to Data Templating Sample window showing the My Task List ListBox with the Priority 1 tasks prominently displayed with a red border.

Tím se dokončí naše diskuze o tomto příkladu. Kompletní ukázku najdete v tématu Úvod do ukázky šablon dat.

Stylování a šablonování prvku ItemsControl

I když ItemsControl se nejedná o jediný typ ovládacího prvku, se kterým můžete použít DataTemplate , je to velmi běžný scénář vytvoření vazby ItemsControl k kolekci. V části Co patří v části DataTemplate jsme probrali, že definice vašeho DataTemplate data by se měla zabývat pouze prezentací dat. Abyste věděli, kdy není vhodné použít DataTemplate , je důležité porozumět různým stylům a vlastnostem šablony, které ItemsControlposkytuje . Následující příklad je navržen tak, aby ilustroval funkci každé z těchto vlastností. V ItemsControl tomto příkladu je vázán na stejnou Tasks kolekci jako v předchozím příkladu. Pro demonstrační účely jsou styly a šablony v tomto příkladu deklarovány jako vložené.

<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>

Následuje snímek obrazovky s příkladem při vykreslení:

ItemsControl example screenshot

Všimněte si, že místo použití ItemTemplate, můžete použít ItemTemplateSelector. Příklad najdete v předchozí části. Podobně místo použití ItemContainerStyle, máte možnost použít ItemContainerStyleSelector.

Dvě další vlastnosti související se stylem ItemsControl , které zde nejsou uvedeny jsou GroupStyle a GroupStyleSelector.

Podpora hierarchických dat

Zatím jsme se podívali pouze na to, jak vytvořit vazbu a zobrazit jedinou kolekci. Někdy máte kolekci, která obsahuje jiné kolekce. Třída HierarchicalDataTemplate je navržená tak, aby se používala s HeaderedItemsControl typy k zobrazení těchto dat. V následujícím příkladu ListLeagueList je seznam League objektů. Každý League objekt má kolekci Name objektů a kolekci Division objektů. Každý Division má kolekci NameTeam objektů a každý Team objekt má .Name

<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>

Příklad ukazuje, že při použití HierarchicalDataTemplatemůžete snadno zobrazit data seznamu, která obsahují další seznamy. Následuje snímek obrazovky s příkladem.

HierarchicalDataTemplate sample screenshot

Viz také