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:
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:
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=TaskName
nastavili XPath@TaskName
(pokud TaskName
je atributem uzlu XML).
Teď vypadá takto ListBox :
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 DataTemplate má DataType 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:
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:
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:
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:
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í:
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 Name
Team
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.
Viz také
.NET Desktop feedback