Přehled šablon dat
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.
Požadavky
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 naleznete v Přehledu datových vazeb .
DataTemplate se týká prezentace dat a je jednou z mnoha funkcí poskytovaných modelem stylů a šablon WPF. Úvod do modelu stylů a šablon WPF, jako je použití Style k nastavení vlastností ovládacích prvků, najdete v tématu Stylingu a Šablonování.
Kromě toho je důležité pochopit Resources
, což jsou v podstatě to, co umožňuje objekty, jako jsou 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č je důležité DataTemplate, projdeme si příklad datové vazby. V tomto příkladu máme ListBox, který je vázán na seznam Task
objektů. Každý objekt Task
má TaskName
(řetězec), Description
(řetězec), Priority
(int) a vlastnost typu TaskType
, což je Enum
s 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 DataTemplate
Bez DataTemplatenáš ListBox momentálně vypadá takto:
cs-CZ:
Co se děje, je to, že ListBox ve výchozím nastavení volá ToString
při pokusu o zobrazení objektů v kolekci. Pokud objekt Task
přepíše metodu ToString
, pak ListBox zobrazí řetězcovou reprezentaci každého zdrojového objektu v podkladové kolekci.
Pokud například třída Task
přepíše metodu ToString
tímto způsobem, kde name
je pole pro vlastnost TaskName
:
public override string ToString()
{
return name.ToString();
}
Public Overrides Function ToString() As String
Return _name.ToString()
End Function
Pak ListBox vypadá takto:
To ale omezuje a nepružné. Také pokud vytváříte vazbu na data XML, nebudete moci přepsat ToString
.
Definování jednoduchého objektu DataTemplate
Řešením je definovat DataTemplate. Jedním ze způsobů, jak toho dosáhnout, je nastavit vlastnost ItemTemplate objektu ListBox na hodnotu DataTemplate. To, co zadáte ve svém DataTemplate se stane vizuální strukturou datového objektu. Následující DataTemplate je poměrně jednoduchý. Dáváme pokyn, aby se každá položka zobrazila jako tři prvky TextBlock v StackPanel. Každý prvek TextBlock je vázán na vlastnost třídy Task
.
<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 Path=TaskName
byste nastavili XPath na @TaskName
(pokud je TaskName
atributem uzlu XML).
Naše ListBox teď vypadá takto:
Vytvoření objektu DataTemplate jako prostředku
V předchozím příkladu jsme definovali DataTemplate jako vložený prvek. 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>
Teď můžete jako prostředek použít myTaskTemplate
, 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}"/>
Vzhledem k tomu, ž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á typ DataTemplate. Jak je znázorněno výše, pro ItemsControl objekty, například ListBox, je to ItemTemplate vlastnost. U ContentControl objektů je to vlastnost ContentTemplate.
Vlastnost DataType
Třída DataTemplate má vlastnost DataType, která je velmi podobná vlastnosti TargetType třídy Style. Proto místo zadání x:Key
pro DataTemplate v předchozím příkladu můžete udělat toto:
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
Tento DataTemplate se automaticky použije pro všechny objekty Task
. Všimněte si, že v tomto případě je x:Key
nastaven implicitně. Proto pokud přiřadíte tuto DataTemplatex:Key
hodnotu, přepisujete implicitní x:Key
a DataTemplate se nepoužije automaticky.
Pokud vytváříte vazbu ContentControl na kolekci Task
objektů, ContentControl nepoužívá výše uvedené DataTemplate automaticky. Důvodem je to, že vazba na ContentControl potřebuje více informací k rozlišení, zda chcete vytvořit vazbu k celé kolekci nebo k jednotlivým objektům. Pokud ContentControl sleduje výběr typu ItemsControl, můžete nastavit vlastnost Path vazby ContentControl na "/
", abyste označili, že vás zajímá aktuální položka. Příklad najdete v tématu Vázání na kolekci a zobrazení informací na základě výběru. V opačném případě je nutné explicitně zadat DataTemplate nastavením vlastnosti ContentTemplate.
Vlastnost DataType je zvláště užitečná, pokud máte CompositeCollection různých typů datových objektů. Příklad najdete 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 Border, 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 s upraveným DataTemplate:
Můžeme nastavit HorizontalContentAlignment na Stretch na ListBox, abychom měli jistotu, že šířka položek zabírá celé místo:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
Vlastnost HorizontalContentAlignment nastavená na Stretch, ListBox teď vypadá takto:
Použití datových spouštěčů pro nastavení hodnot vlastností
Současná prezentace nám neřekne, jestli je Task
domácí nebo kancelářský úkol. Nezapomeňte, že objekt Task
má TaskType
vlastnost typu TaskType
, což je výčet s hodnotami Home
a Work
.
V následujícím příkladu DataTrigger nastaví BorderBrush elementu s názvem border
na Yellow
pokud je vlastnost TaskType
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>
Naše aplikace teď vypadá takto. Domácí úkoly mají žluté ohraničení a kancelářské úkoly mají tyrkysové ohraničení.
V tomto příkladu DataTrigger používá Setter k nastavení hodnoty vlastnosti. Třídy triggeru mají také vlastnosti EnterActions a 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 vlastnost TaskType
a pomocí převaděče hodnot vrátit barvu na základě hodnoty TaskType
. 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. Pro více informací o tom, jak napsat převaděč, viz IValueConverter.
Co patří do objektu DataTemplate?
V předchozím příkladu jsme trigger umístili do DataTemplate pomocí vlastnosti DataTemplate.Triggers. Hodnota vlastnosti elementu Border, který se nachází v rámci DataTemplate, je nastavena pomocí triggeru Setter. Pokud vlastnosti, o které se Setters
zajímá, nejsou vlastnostmi prvků aktuálního DataTemplate, může být vhodnější nastavit vlastnosti pomocí Style určeného pro třídu ListBoxItem (pokud je ovládací prvek, k němuž vytváříte vazbu, ListBox). Pokud například chcete, aby Trigger animoval Opacity hodnotu položky, když myš ukazuje na položku, definujete triggery v rámci stylu ListBoxItem. Příklad najdete v ukázce Úvod do stylování a šablonování.
Obecně mějte na paměti, že DataTemplate se používá u každého vygenerovaného ListBoxItem (další informace o tom, jak a kde se skutečně používá, najdete na stránce ItemTemplate.) Vaše DataTemplate se zabývá pouze prezentací a vzhledem 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 stanoví položky, nepatří do definice DataTemplate. Příklad najdete v oddílu Styling a Šablonování ItemsControl.
Výběr objektu DataTemplate na základě vlastností datového objektu
V Části Vlastnost DataType jsme probrali, že můžete definovat různé šablony dat pro různé datové objekty. To je zvlášť užitečné, pokud máte CompositeCollection různých typů nebo kolekcí s položkami různých typů. V části Použít DataTrigger k aplikaci hodnot vlastností jsme ukázali, že pokud máte kolekci stejného typu datových objektů, můžete vytvořit DataTemplate a potom pomocí triggerů aplikovat 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 mohou vyžadovat vytvoření jiného DataTemplate pro datové objekty, které mají stejný typ, ale mají různé vlastnosti.
Pokud má například objekt Task
hodnotu Priority
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 pro zobrazení objektů s vysokou prioritou Task
. Pojďme do oddílu prostředků přidat následující DataTemplate:
<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á vlastnost DataTemplate.Resources. Prostředky definované v této sekci jsou sdíleny prvkem v rámci DataTemplate.
Implementujte logiku pro výběr, kterou DataTemplate použít k použití na základě Priority
hodnoty datového objektu, vytvořte podtřídu DataTemplateSelector a přepište metodu SelectTemplate. V následujícím příkladu metoda SelectTemplate poskytuje logiku pro vrácení příslušné šablony na základě hodnoty vlastnosti Priority
. Šablona, která se má vrátit, se nachází v prostředcích obálkového prvku 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
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 vlastnosti ItemTemplateSelector u ListBox.
ListBox volá metodu SelectTemplate z TaskListDataTemplateSelector
pro každou položku v rámci podkladové kolekce. Volání předá datový objekt jako parametr položky.
DataTemplate 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"/>
S použitím selektoru šablony nyní ListBox vypadá takto:
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 není jediným typem ovládacího prvku, se kterým můžete DataTemplate použít, je to velmi běžný scénář vytvoření vazby ItemsControl k kolekci. V části What Belongs in a DataTemplate jsme probrali, že definice vašeho DataTemplate by se měla zabývat pouze prezentací dat. Abyste věděli, kdy není vhodné použít DataTemplate je důležité pochopit různé vlastnosti stylu a šablony poskytované ItemsControl. Následující příklad je navržen tak, aby ilustroval funkci každé z těchto vlastností.
ItemsControl v tomto příkladu je svázán se stejnou kolekcí Tasks
jako v předchozím příkladu. Pro demonstrační účely jsou styly a šablony v tomto příkladu deklarovány přímo.
<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, jak se zobrazí:
Všimněte si, že místo použití ItemTemplatemůžete použít ItemTemplateSelector. Příklad najdete v předchozí části. Podobně místo použití ItemContainerStylemá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 navázat a zobrazit jedinou kolekci. Někdy máte kolekci, která obsahuje jiné kolekce. Třída HierarchicalDataTemplate je navržená tak, aby byla použita s typy HeaderedItemsControl k zobrazení těchto dat. V následujícím příkladu je ListLeagueList
seznam League
objektů. Každý objekt League
má Name
a kolekci objektů Division
. Každý Division
má Name
a kolekci objektů Team
a každý objekt Team
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.
Ukázkový snímek obrazovky HierarchicalDataTemplate
Viz také
.NET Desktop feedback