Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A WPF adatmegjelenítési modell nagy rugalmasságot biztosít az adatok megjelenítésének meghatározásához. A WPF-vezérlők beépített funkciókkal rendelkeznek az adatbemutató testreszabásának támogatásához. Ez a témakör először bemutatja, hogyan definiálhat, DataTemplate majd bevezethet más adatátrendezési funkciókat, például az egyéni logikán alapuló sablonok kiválasztását és a hierarchikus adatok megjelenítésének támogatását.
Előfeltételek
Ez a témakör az adatsablonozási funkciókra összpontosít, és nem az adatkötési koncepciók bevezetője. Az alapvető adatkötési fogalmakkal kapcsolatos információkért tekintse meg az Adatkötés áttekintése című témakört.
DataTemplate az adatok bemutatásáról szól, és egyike a WPF-stílus- és templatingmodell által biztosított számos funkciónak. A WPF-stílus- és templatingmodell bemutatása, például a tulajdonságok vezérlőkre való beállításának módját Style a Stílus és a Templating témakör ismerteti.
Emellett fontos megérteni Resources
, amelyek lényegében lehetővé teszik az olyan objektumok, mint például Style és DataTemplate, újrahasználatát. További információ az erőforrásokról: XAML-erőforrások.
A data templating alapjai
Annak bemutatásához, hogy miért DataTemplate fontos, tekintsünk át egy adatkötési példát. Ebben a példában van egy ListBox, amely egy Task
objektumlistához van kötve. Minden Task
objektumhoz tartozik egy TaskName
(sztring), egy Description
(sztring), egy Priority
(int), és egy TaskType
tulajdonság, amely egy Enum
típusú, Home
és Work
értékekkel.
<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>
DataTemplate nélkül
Nélkül a DataTemplate, a ListBox jelenlegi állapotunk így néz ki:
Az történik, hogy konkrét utasítások nélkül a ListBox alapértelmezés szerint meghívja a ToString
függvényt, amikor megjeleníti az objektumokat a gyűjteményben. Ezért ha az Task
objektum felülbírálja a ToString
metódust, akkor a ListBox mögöttes gyűjteményben az egyes forrásobjektumok sztringképe jelenik meg.
Ha például az Task
osztály felülbírálja a ToString
metódust így, ahol a name
mező a TaskName
tulajdonsághoz tartozik:
public override string ToString()
{
return name.ToString();
}
Public Overrides Function ToString() As String
Return _name.ToString()
End Function
Ezután a ListBox következőképpen néz ki:
Azonban korlátozó és rugalmatlan. Továbbá, ha XML-adatokhoz kötődne, nem tudná felülbírálni ToString
.
Egyszerű DataTemplate definiálása
A megoldás az, hogy meghatározzunk egy DataTemplate. Ennek egyik módja, ha a ItemTemplate tulajdonságot a ListBox egy DataTemplate-ra állítjuk. A megadott DataTemplate adatok az adatobjektum vizuális struktúrájává válnak. Az alábbiak DataTemplate meglehetősen egyszerűek. Útmutatást adunk, hogy minden elem három TextBlock elemként jelenik meg egy adott elemen StackPanelbelül. Minden TextBlock elem az osztály egy tulajdonságához Task
van kötve.
<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>
A jelen témakör példáinak alapjául szolgáló adatok CLR-objektumok gyűjteményei. Ha XML-adatokhoz köti, az alapvető fogalmak megegyeznek, de van némi szintaktikai különbség. Például, ha az XML-csomópont egy attribútuma Path=TaskName
, akkor ahelyett, hogy XPath lenne, beállítaná @TaskName
értékre TaskName
.
ListBox Most a következőhöz hasonlóan néz ki:
A DataTemplate létrehozása erőforrásként
A fenti példában soron belül definiáltuk a DataTemplate. Gyakoribb, hogy az erőforrások szakaszban definiáljuk, hogy újrahasználható objektum lehessen, ahogy az alábbi példában is látható:
<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>
Most már használhatja myTaskTemplate
erőforrásként, ahogy az alábbi példában is látható:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
Mivel myTaskTemplate
egy erőforrás, most már más vezérlőkön is használhatja, amelyek DataTemplate típusú értéket vesznek fel. Ahogy fentebb látható, az olyan objektumok esetében, mint például a ItemsControl, a ListBox objektumnál ez a ItemTemplate tulajdonság érvényes.
ContentControl objektumok esetében a ContentTemplate tulajdonság.
A DataType tulajdonság
Az DataTemplate osztálynak DataType olyan tulajdonsága van, amely nagyon hasonlít az TargetTypeStyle osztály tulajdonságához. Ezért a fenti példában a x:Key
megadásának helyett megteheti a következőket:
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
Ezt DataTemplate a rendszer automatikusan alkalmazza az összes Task
objektumra. Vegye figyelembe, hogy ebben az esetben a x:Key
beállítás implicit módon történik. Ezért ha ezt DataTemplate az x:Key
értéket rendeli hozzá, felülírja az implicit x:Key
értéket, és az DataTemplate nem lesz automatikusan alkalmazva.
Ha egy ContentControl-t köt egy Task
objektumok gyűjteményéhez, a ContentControl a fent DataTemplate-et nem használja automatikusan. Ennek az az oka, hogy a ContentControl kötéshez több információra van szükség, hogy megkülönböztethessük, hogy egy teljes gyűjteményhez vagy egyes objektumokhoz akarunk-e kötni.
ContentControl Ha az ItemsControl típus kiválasztását követi nyomon, a Path kötés ContentControl tulajdonságát "/
" értékre állíthatja, hogy jelezze, hogy érdekli az aktuális elem. Egy példa: Egy gyűjteményhez való kötés és információ megjelenítése a kijelölés alapján. Ellenkező esetben explicit módon kell megadnia a DataTemplate-t a ContentTemplate tulajdonság beállításával.
A DataType tulajdonság különösen akkor hasznos, ha különböző típusú adatobjektumokkal rendelkezik CompositeCollection . Példa: CompositeCollection implementálása.
Továbbiak hozzáadása a DataTemplate-hoz
Jelenleg az adatok a szükséges információkkal jelennek meg, de határozottan van még hova fejlődni. Javítsunk a bemutatón egy Border, egy Grid és néhány TextBlock elem hozzáadásával, amelyek leírják a megjelenített adatokat.
<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>
Az alábbi képernyőképen a ListBox látható a módosított DataTemplate-vel.
HorizontalContentAlignment Stretch Beállíthatjuk, ListBox hogy az elemek szélessége a teljes területet elfoglalja:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
A(z) HorizontalContentAlignment tulajdonság értéke Stretch-re állítva, a ListBox most így néz ki:
Tulajdonságértékek alkalmazása DataTriggers használatával
Az aktuális bemutató nem árulja el, hogy otthoni vagy irodai feladatról van-e Task
szó. Ne feledje, hogy az Task
objektumnak van egy TaskType
típustulajdonsága TaskType
, amely egy számbavétel értékekkel Home
és Work
.
Az alábbi példában a DataTrigger beállítja az BorderBrush elem border
értékét, ha a Yellow
tulajdonság TaskType
.
<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>
Az alkalmazás most a következőhöz hasonlóan néz ki. Az otthoni feladatok sárga szegélylel jelennek meg, az irodai feladatok pedig egy aqua szegélyrel jelennek meg:
Ebben a példában a DataTrigger egy Setter-et használ egy tulajdonságérték beállításához. Az eseményindítóosztályok olyan tulajdonságokkal is rendelkeznek EnterActionsExitActions , amelyek lehetővé teszik műveletek, például animációk indítását. Emellett van egy MultiDataTrigger osztály is, amely lehetővé teszi a módosítások alkalmazását több adathoz kötött tulajdonságérték alapján.
Ugyanezt a hatást úgy érheti el, ha a tulajdonságot a BorderBrushTaskType
tulajdonsághoz köti, és egy értékkonverter használatával visszaadja a színt az TaskType
érték alapján. A fenti effektus konverterrel történő létrehozása teljesítmény szempontjából kissé hatékonyabb. Emellett a saját konverter létrehozása nagyobb rugalmasságot biztosít, mivel saját logikát biztosít. Végső soron a választott technika a forgatókönyvtől és a preferenciától függ. További információt a konverter írásáról a IValueConverter témakörben talál.
Mi tartozik a DataTemplate-hez?
Az előző példában az eseményindítót a DataTemplate tulajdonság segítségével a DataTemplate.Triggers-ban helyeztük el. Az Setter eseményindító beállítja egy elem (az Border elem) tulajdonságának értékét, amely a DataTemplate -en belül található. Ha azonban az Ön Setters
-t érintő tulajdonságok nem az aktuális DataTemplate elemeinek tulajdonságai, akkor célszerűbb lehet ezeknek a tulajdonságoknak a beállítása egy, a Style osztályhoz tartozó ListBoxItem segítségével (ha a kötött vezérlőelem egy ListBox). Például, ha azt szeretné Trigger, hogy az Opacity értéke animálódjon, amikor az egér egy elemre mutat, akkor a ListBoxItem stíluson belül definiálja az eseményindítókat. Példa: Bevezetés a stílus- és templatálási mintába.
Általában tartsa észben, hogy az DataTemplate mindegyik előállított ListBoxItem-re alkalmazva van (a tényleges alkalmazási módjáról és helyéről további információt az ItemTemplate oldalon talál). Az Ön DataTemplate csak az adatok megjelenítésével és kinézetével foglalkozik. A legtöbb esetben a bemutató összes egyéb aspektusa, például hogy néz ki egy elem a kijelöléskor, vagy hogyan ListBox rendezi az elemeket, nem tartoznak a DataTemplate definíciójába. Például tekintse meg az ItemsControl stílus- és templatálási szakaszát .
DataTemplate kiválasztása az adatobjektum tulajdonságai alapján
A DataType Tulajdonság szakaszban bemutattuk, hogy különböző adatsablonokat definiálhat különböző adatobjektumokhoz. Ez különösen akkor hasznos, ha különböző típusú vagy elemeket tartalmazó gyűjteményekkel rendelkezik CompositeCollection. A Tulajdonságértékek alkalmazása a DataTriggers használata szakaszban azt mutattuk be, hogy ha azonos típusú adatobjektum-gyűjteménysel rendelkezik, létrehozhat egy DataTemplate eseményindítót, majd triggerekkel alkalmazhatja a módosításokat az egyes adatobjektumok tulajdonságértékei alapján. Az eseményindítók azonban lehetővé teszik a tulajdonságértékek alkalmazását vagy animációk indítását, de nem teszik lehetővé az adatobjektumok szerkezetének rekonstruálását. Egyes forgatókönyvek esetében előfordulhat, hogy más DataTemplate típusú, de eltérő tulajdonságokkal rendelkező adatobjektumokat kell létrehoznia.
Ha például egy Task
objektum Priority
értéke 1
van, érdemes lehet teljesen más megjelenést adni neki, hogy riasztásként szolgáljon saját magának. Ebben az esetben létrehoz egy DataTemplate objektumot a magas prioritású Task
objektumok megjelenítéséhez. Vegyük fel a következőket DataTemplate az erőforrások szakaszba:
<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>
Ez a példa a DataTemplate.Resources tulajdonságot használja. Az ebben a szakaszban definiált erőforrásokat a DataTemplate elemein belüliek osztják meg.
Annak érdekében, hogy logikát biztosítson az adatobjektum értéke alapján választható DataTemplate kiválasztásához, hozzon létre egy Priority
alosztályt, és írja felül a DataTemplateSelector metódust. Az alábbi példában a metódus logikát SelectTemplate biztosít a megfelelő sablon visszaadásához a Priority
tulajdonság értéke alapján. A visszaadandó sablon a burkolóelem Window erőforrásaiban található.
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
Ezt követően deklarálhatjuk erőforrásként TaskListDataTemplateSelector
:
<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>
A sablonválasztó erőforrás használatához rendelje hozzá a ItemTemplateSelector tulajdonságához a ListBox-t. A ListBox rendszer meghívja az SelectTemplateTaskListDataTemplateSelector
alapul szolgáló gyűjtemény egyes elemeinek metódusát. A hívás elemparaméterként továbbítja az adatobjektumot. A DataTemplate metódus által visszaadott érték ezután az adott adatobjektumra lesz alkalmazva.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"/>
Miután a sablonválasztó be van állítva, a ListBox így jelenik meg:
Ezzel lezárul a példa megvitatása. A teljes minta megtekintéséhez lásd: Bevezetés az adatsablonozási mintába.
ItemsControl stílusa és átformázása
Annak ellenére, hogy a ItemsControl nem az egyetlen vezérlőtípus, amellyel a DataTemplate használható, nagyon gyakori, hogy a ItemsControl egy gyűjteményhez van kötve. A What Belongs in a DataTemplate (Mi tartozik a DataTemplate-ban ) szakaszban azt tárgyaltuk, hogy az Ön DataTemplate definíciójának csak az adatok bemutatásával kell foglalkoznia. Annak érdekében, hogy tudjuk, mikor nem megfelelő a DataTemplate használata, fontos megérteni a különböző stílus- és sablontulajdonságokat, amelyeket a ItemsControl. Az alábbi példa ezeknek a tulajdonságoknak a függvényét szemlélteti. Az ItemsControl ebben a példában szereplő gyűjtemény ugyanahhoz Tasks
a gyűjteményhez van kötve, mint az előző példában. Bemutató célokra a példában szereplő stílusok és sablonok mindegyike beágyazottként van deklarálva.
<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>
A következő képernyőkép a példa renderelésekor látható:
Vegye figyelembe, hogy ahelyett, hogy a függvényt ItemTemplatehasználja, használhatja a ItemTemplateSelector. Egy példaért tekintse meg az előző szakaszt. Hasonlóképpen, ahelyett, hogy a ItemContainerStyle-t használja, lehetőséged van arra, hogy a ItemContainerStyleSelector-t használd.
Az itt nem látható ItemsControl két másik stílushoz kapcsolódó tulajdonsága a GroupStyle és a GroupStyleSelector.
Hierarchikus adatok támogatása
Eddig csak azt vizsgáltuk, hogyan kössünk és jelenítsünk meg egyetlen gyűjteményt. Néha van egy gyűjtemény, amely más gyűjteményeket tartalmaz. Az HierarchicalDataTemplate osztály úgy lett kialakítva, hogy az ilyen adatok megjelenítéséhez a HeaderedItemsControl típusokkal használható legyen. Az alábbi példában ListLeagueList
egy League
objektumokból álló lista. Minden League
objektum rendelkezik egy Name
és egy objektumgyűjteménysel Division
. Mindegyik Division
rendelkezik egy Name
és egy objektumgyűjteménysel Team
, és mindegyik Team
objektumhoz tartozik egy 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>
A példa azt mutatja, hogy a használatával HierarchicalDataTemplateegyszerűen megjelenítheti az egyéb listákat tartalmazó listaadatokat. Az alábbiakban egy képernyőkép látható a példáról.
Lásd még
.NET Desktop feedback