Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Il modello di modellazione dei dati WPF offre una grande flessibilità per definire la presentazione dei dati. I controlli WPF dispongono di funzionalità predefinite per supportare la personalizzazione della presentazione dei dati. Questo argomento illustra innanzitutto come definire un DataTemplate oggetto e quindi introduce altre funzionalità di creazione di modelli di dati, ad esempio la selezione di modelli in base alla logica personalizzata e il supporto per la visualizzazione di dati gerarchici.
Prerequisiti
Questo argomento è incentrato sulle funzionalità di creazione di modelli di dati e non è un'introduzione dei concetti relativi al data binding. Per informazioni sui concetti di base sul data binding, vedere la Panoramica del data binding.
DataTemplate riguarda la presentazione dei dati ed è una delle numerose funzionalità fornite dal modello di applicazione di stili e modelli WPF. Per un'introduzione al modello di stilizzazione e templating WPF, ad esempio su come utilizzare un Style per impostare le proprietà sui controlli, vedere l'argomento Styling and Templating.
Inoltre, è importante comprendere Resources
, che sono essenzialmente ciò che abilita oggetti come Style e DataTemplate per essere riutilizzabili. Per altre informazioni sulle risorse, vedi Risorse XAML.
Nozioni di base sulla creazione di modelli di dati
Per illustrare perché DataTemplate è importante, verrà illustrato un esempio di data binding. In questo esempio è presente un oggetto ListBox associato a un elenco di Task
oggetti . Ogni Task
oggetto ha una TaskName
(stringa), una Description
(stringa), un Priority
(int) e una proprietà di tipo TaskType
, che è un Enum
oggetto con valori Home
e 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>
Senza DataTemplate
Senza un DataTemplate, il nostro ListBox attualmente ha questo aspetto:
Ciò che accade è che, senza istruzioni specifiche, ListBox chiama ToString
di default quando si tenta di visualizzare gli oggetti nella raccolta. Pertanto, se l'oggetto Task
esegue l'override del metodo ToString
, allora ListBox visualizza la rappresentazione in forma di stringa di ogni oggetto di origine nella collezione sottostante.
Ad esempio, se la classe Task
esegue l'override del metodo ToString
in questo modo, dove name
è il campo per la proprietà TaskName
:
public override string ToString()
{
return name.ToString();
}
Public Overrides Function ToString() As String
Return _name.ToString()
End Function
L'aspetto ListBox sarà quindi simile al seguente:
Tuttavia, questo è limitante e inflessibile. Inoltre, se si esegue il binding ai dati XML, non sarà possibile eseguire l'override di ToString
.
Definizione di un oggetto DataTemplate semplice
La soluzione consiste nel definire un oggetto DataTemplate. Un modo per eseguire questa operazione consiste nell'impostare la proprietà ItemTemplate di ListBox su un oggetto DataTemplate. Ciò che si specifica in DataTemplate diventa la struttura visiva dell'oggetto dati. Il seguente DataTemplate è piuttosto semplice. Stiamo fornendo istruzioni affinché ogni elemento appaia come tre TextBlock all'interno di un StackPanel. Ogni TextBlock elemento è associato a una proprietà della Task
classe .
<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>
I dati sottostanti per gli esempi in questo argomento sono una raccolta di oggetti CLR. Se si esegue il binding ai dati XML, i concetti fondamentali sono gli stessi, ma esiste una leggera differenza sintattica. Ad esempio, anziché avere Path=TaskName
, dovresti impostare XPath su @TaskName
(se TaskName
è un attributo del tuo nodo XML).
Ora il nostro ListBox ha l'aspetto simile al seguente:
Creazione di DataTemplate come risorsa
Nell'esempio precedente, abbiamo definito DataTemplate inline. È più comune definirlo nella sezione resources in modo che possa essere un oggetto riutilizzabile, come nell'esempio seguente:
<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>
È ora possibile usare myTaskTemplate
come risorsa, come nell'esempio seguente:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
Poiché myTaskTemplate
è una risorsa, è ora possibile usarla in altri controlli con una proprietà che accetta un DataTemplate tipo. Come illustrato in precedenza, per gli oggetti ItemsControl, come ListBox, è la proprietà ItemTemplate. Per gli oggetti ContentControl, è la proprietà ContentTemplate.
Proprietà DataType
La DataTemplate classe ha una DataType proprietà molto simile alla TargetType proprietà della Style classe . Pertanto, invece di specificare un x:Key
per nell'esempio DataTemplate precedente, è possibile eseguire le operazioni seguenti:
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
Questa operazione DataTemplate viene applicata automaticamente a tutti gli Task
oggetti. Si noti che in questo caso l'oggetto x:Key
viene impostato in modo implicito. Pertanto, se si assegna a questo DataTemplate un valore x:Key
, si esegue l'override del valore implicito x:Key
e il valore DataTemplate non verrà applicato automaticamente.
Se si associa un ContentControl a una raccolta di oggetti Task
, il ContentControl non utilizza automaticamente il DataTemplate. Ciò è dovuto al fatto che l'associazione su un oggetto ContentControl necessita di ulteriori informazioni per distinguere se si desidera eseguire l'associazione a un'intera raccolta o a singoli oggetti. Se il tuo ContentControl sta tenendo traccia della selezione di un tipo di ItemsControl, puoi impostare la proprietà Path del binding ContentControl su "/
" per indicare che ti interessa l'elemento corrente. Per un esempio, vedere Associare a una raccolta e visualizzare informazioni in base alla selezione. In caso contrario, è necessario specificare il DataTemplate esplicitamente impostando la proprietà ContentTemplate.
La DataType proprietà è particolarmente utile quando si dispone di diversi CompositeCollection tipi di oggetti dati. Per un esempio, vedere Implementare un oggetto CompositeCollection.
Aggiunta di ulteriori elementi al DataTemplate
Attualmente i dati appaiono con le informazioni necessarie, ma c'è sicuramente spazio per il miglioramento. Per migliorare la presentazione, aggiungere un Border, un Grid e alcuni TextBlock elementi che descrivono gli dati visualizzati.
<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>
Lo screenshot seguente mostra il ListBox modificato con questo DataTemplate.
È possibile impostare HorizontalContentAlignment su Stretch su ListBox per assicurarsi che la larghezza degli elementi occupi l'intero spazio:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
Con la HorizontalContentAlignment proprietà impostata su Stretch, l'oggetto ListBox è ora simile al seguente:
Usare DataTriggers per applicare i valori delle proprietà
La presentazione corrente non indica se un oggetto Task
è un'attività principale o un'attività di ufficio. Tenere presente che l'oggetto Task
ha una TaskType
proprietà di tipo TaskType
, che è un'enumerazione con valori Home
e Work
.
Nell'esempio seguente, il DataTrigger imposta la BorderBrush dell'elemento denominato border
su Yellow
se la proprietà 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>
L'applicazione avrà ora un aspetto simile al seguente. Le attività home vengono visualizzate con un bordo giallo e le attività di ufficio vengono visualizzate con un bordo aqua:
In questo esempio DataTrigger usa Setter per impostare il valore di una proprietà. Le classi trigger hanno anche le EnterActions proprietà e ExitActions che consentono di avviare un set di azioni, ad esempio le animazioni. È inoltre disponibile una MultiDataTrigger classe che consente di applicare modifiche in base a più valori di proprietà associati a dati.
Un modo alternativo per ottenere lo stesso effetto consiste nell'associare la BorderBrush proprietà alla TaskType
proprietà e usare un convertitore di valori per restituire il colore in base al TaskType
valore. La creazione dell'effetto precedente tramite un convertitore è leggermente più efficiente in termini di prestazioni. Inoltre, la creazione di un convertitore personalizzato offre maggiore flessibilità perché si fornisce una logica personalizzata. In definitiva, la tecnica scelta dipende dallo scenario e dalle preferenze. Per informazioni su come scrivere un convertitore, vedere IValueConverter.
Che cosa è incluso in un DataTemplate?
Nell'esempio precedente il trigger è stato inserito all'interno di DataTemplate usando la DataTemplate.Triggers proprietà . L'oggetto Setter del trigger imposta il valore di una proprietà di un elemento (l'elemento Border ) che si trova all'interno di DataTemplate. Tuttavia, se le proprietà a cui il tuo Setters
è interessato non sono proprietà di elementi che si trovano all'interno del DataTemplate corrente, potrebbe essere più opportuno impostare le proprietà usando un Style specifico per la classe ListBoxItem (se il controllo a cui si sta eseguendo l'associazione è un ListBox). Ad esempio, se vuoi che Trigger animi il Opacity valore dell'elemento quando un mouse punta a un elemento, definisci i attivatori all'interno di un ListBoxItem stile. Per un esempio, vedere Introduzione allo Styling e al Templating Sample.
In generale, tenere presente che il DataTemplate viene applicato a ognuno dei ListBoxItem generati (per ulteriori informazioni su come e dove viene effettivamente applicato, vedere la pagina ItemTemplate). L'oggetto DataTemplate si occupa solo della presentazione e dell'aspetto degli oggetti di dati. Nella maggior parte dei casi, tutti gli altri aspetti della presentazione, ad esempio l'aspetto di un elemento quando viene selezionato o come ListBox dispone gli elementi, non appartengono alla definizione di un oggetto DataTemplate. Per un esempio, vedere la sezione Stile e modelli di ItemsControl.
Scelta di un oggetto DataTemplate in base alle proprietà dell'oggetto dati
Nella sezione Proprietà DataType è stato illustrato come definire modelli di dati diversi per oggetti dati diversi. Ciò è particolarmente utile quando si dispone di tipi diversi CompositeCollection o di raccolte con elementi di tipi diversi. Nella sezione Use DataTriggers to Apply Property Values (Usa DataTriggers per applicare i valori delle proprietà ) è stato mostrato che se si dispone di una raccolta dello stesso tipo di oggetti dati è possibile creare un DataTemplate oggetto e quindi usare trigger per applicare le modifiche in base ai valori delle proprietà di ogni oggetto dati. Tuttavia, i trigger consentono di applicare valori di proprietà o animazioni di avvio, ma non offrono la flessibilità necessaria per ricostruire la struttura degli oggetti dati. Alcuni scenari possono richiedere la creazione di un oggetto diverso DataTemplate per gli oggetti dati dello stesso tipo, ma con proprietà diverse.
Ad esempio, quando un Task
oggetto ha un Priority
valore di 1
, può essere necessario assegnargli un aspetto completamente diverso per fungere da avviso per se stessi. In tal caso, si crea un oggetto DataTemplate per la visualizzazione degli oggetti con priorità Task
alta. Aggiungere quanto segue DataTemplate alla sezione resources:
<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>
In questo esempio viene utilizzata la proprietà DataTemplate.Resources . Le risorse definite in tale sezione vengono condivise dagli elementi all'interno di DataTemplate.
Per fornire la logica per scegliere quale DataTemplate utilizzare in base al Priority
valore dell'oggetto dati, creare una sottoclasse di DataTemplateSelector ed eseguire l'override del SelectTemplate metodo. Nell'esempio seguente il SelectTemplate metodo fornisce la logica per restituire il modello appropriato in base al valore della Priority
proprietà . Il modello da restituire si trova nelle risorse dell'elemento avvolgente 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
Possiamo quindi dichiarare TaskListDataTemplateSelector
come una risorsa.
<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>
Per usare la risorsa del selettore di modelli, assegnarla alla proprietà ItemTemplateSelector di ListBox. Il ListBox chiama il metodo SelectTemplate del TaskListDataTemplateSelector
per ciascuno degli elementi nella raccolta sottostante. La chiamata passa l'oggetto dati come parametro dell'elemento. L'oggetto DataTemplate restituito dal metodo viene quindi applicato all'oggetto dati.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"/>
Con il selettore di modello sul posto, il ListBox ora appare come segue:
Questo conclude la discussione su questo esempio. Per l'esempio completo, vedere Introduction to Data Templating Sample (Introduzione all'esempio di modelli di dati).
Applicazione di stili e modellazione a ItemsControl
Anche se ItemsControl non è l'unico tipo di controllo con cui è possibile usare un DataTemplate, si tratta di uno scenario molto comune per collegare un ItemsControl a una raccolta. Nella sezione What Belongs in a DataTemplate è stato illustrato che la definizione del tuo DataTemplate dovrebbe occuparsi solo della presentazione dei dati. Per sapere quando non è adatto per usare un DataTemplate è importante comprendere le diverse proprietà di stile e modello fornite da ItemsControl. L'esempio seguente è progettato per illustrare la funzione di ognuna di queste proprietà. Il ItemsControl in questo esempio è vincolato alla stessa raccolta Tasks
dell'esempio precedente. A scopo dimostrativo, gli stili e i modelli in questo esempio sono tutti dichiarati inline.
<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>
Di seguito è riportato uno screenshot dell'esempio quando viene eseguito il rendering:
Si noti che invece di usare ItemTemplate, è possibile usare ItemTemplateSelector. Per un esempio, vedere la sezione precedente. Analogamente, invece di usare il ItemContainerStyle, è possibile usare il ItemContainerStyleSelector.
Altre due proprietà correlate allo stile di ItemsControl che non sono illustrate di seguito sono GroupStyle e GroupStyleSelector.
Supporto per i dati gerarchici
Finora abbiamo esaminato solo come associare e visualizzare una singola raccolta. In alcuni casi si dispone di una raccolta che contiene altre raccolte. La HierarchicalDataTemplate classe è progettata per essere usata con HeaderedItemsControl i tipi per visualizzare tali dati. Nell'esempio seguente è ListLeagueList
riportato un elenco di League
oggetti . Ogni League
oggetto ha un Name
e una collezione di Division
oggetti. Ogni Division
ha un Name
e una raccolta di oggetti Team
, e ogni oggetto Team
ha un 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>
L'esempio mostra che con l'uso di è possibile visualizzare facilmente i dati dell'elenco HierarchicalDataTemplateche contengono altri elenchi. Lo screenshot seguente mostra l'esempio.
Vedere anche
- associazione dei dati
- Trova Elementi DataTemplate-Generated
- Stile e Templatizzazione
- Panoramica del Data Binding
- Panoramica modelli e stili di intestazione di colonna GridView
.NET Desktop feedback