Vue d’ensemble des modèles de données
Le modèle de création de modèles de données WPF offre une grande souplesse pour définir la présentation des données. Les contrôles WPF possèdent des fonctionnalités intégrées permettant de prendre en charge la personnalisation de la présentation des données. Cette rubrique montre tout d’abord comment définir une DataTemplate autre fonctionnalité de création de modèles de données, comme la sélection de modèles basés sur une logique personnalisée et la prise en charge de l’affichage des données hiérarchiques.
Prérequis
Cette rubrique porte sur les fonctionnalités de création de modèles de données ; elle ne constitue pas une introduction aux concepts de liaison de données. Pour plus d’informations sur les concepts de base de la liaison de données, consultez la Vue d’ensemble de la liaison de données.
DataTemplate est à propos de la présentation des données et est l’une des nombreuses fonctionnalités fournies par le modèle de style et de création de modèles WPF. Pour obtenir une présentation du modèle de style et de création de modèles WPF, par exemple comment utiliser un Style pour définir des propriétés sur des contrôles, consultez la rubrique Style et Création de modèles.
En outre, il est important de comprendre Resources
, qui sont essentiellement ce qui permet aux objets tels que Style et DataTemplate d’être réutilisables. Pour plus d’informations sur les ressources, consultez la page Ressources XAML.
Informations de base sur la création de modèles de données
Pour illustrer pourquoi DataTemplate il est important, passons en revue un exemple de liaison de données. Dans cet exemple, nous avons un ListBox objet lié à une liste d’objets Task
. Chaque objet Task
a un TaskName
(chaîne), une Description
(chaîne), une Priority
(entier) et une propriété de type TaskType
, qui est un Enum
avec des valeurs Home
et 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>
Sans DataTemplate
Sans un DataTemplate, notre ListBox apparence actuelle ressemble à ceci :
Ce qui se passe, c’est que sans instructions spécifiques, les ListBox appels ToString
par défaut lors de la tentative d’affichage des objets dans la collection. Par conséquent, si l’objet Task
remplace la ToString
méthode, l’objet ListBox affiche la représentation sous-jacente de chaque objet source.
Par exemple, si la classe Task
remplace la méthode ToString
de cette façon, où name
correspond au champ de la propriété TaskName
:
public override string ToString()
{
return name.ToString();
}
Public Overrides Function ToString() As String
Return _name.ToString()
End Function
Ensuite, le ListBox résultat ressemble à ce qui suit :
Toutefois, cet affichage est restrictif et rigide. En outre, si vous effectuez une liaison à des données XML, vous ne pouvez pas remplacer ToString
.
Définir un DataTemplate simple
La solution consiste à définir un DataTemplate. Une façon de le faire est de définir la ItemTemplate propriété de l’objet ListBox sur un DataTemplate. Ce que vous spécifiez dans votre DataTemplate devient la structure visuelle de votre objet de données. Ce qui suit DataTemplate est assez simple. Nous donnons des instructions indiquant que chaque élément apparaît sous la forme de trois TextBlock éléments dans un StackPanel. Chaque TextBlock élément est lié à une propriété de la 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>
Les données sous-jacentes des exemples de cette rubrique sont une collection d’objets CLR. Si vous effectuez une liaison à des données XML, les concepts fondamentaux sont identiques, mais il existe une légère différence syntactique. Par exemple, au lieu d’avoir Path=TaskName
, vous devez définir @TaskName
XPath sur (s’il s’agit TaskName
d’un attribut de votre nœud XML).
Maintenant, notre ListBox apparence ressemble à ce qui suit :
Créer un DataTemplate comme ressource
Dans l’exemple ci-dessus, nous avons défini l’inline DataTemplate . Il est plus courant de le définir dans la section des ressources afin d’en faire un objet réutilisable, comme dans l’exemple suivant :
<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>
Vous pouvez à présent utiliser myTaskTemplate
comme ressource, comme dans l’exemple suivant :
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
Étant donné qu’il myTaskTemplate
s’agit d’une ressource, vous pouvez désormais l’utiliser sur d’autres contrôles qui ont une propriété qui prend un DataTemplate type. Comme indiqué ci-dessus, pour ItemsControl les objets, tels que le ListBox, il s’agit de la ItemTemplate propriété. Pour ContentControl les objets, il s’agit de la ContentTemplate propriété.
La propriété DataType
La DataTemplate classe a une DataType propriété très similaire à la TargetType propriété de la Style classe. Par conséquent, au lieu de spécifier une x:Key
pour l’exemple DataTemplate ci-dessus, vous pouvez effectuer les opérations suivantes :
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
Cette opération DataTemplate est appliquée automatiquement à tous les Task
objets. Notez que, dans ce cas, la x:Key
est définie implicitement. Par conséquent, si vous affectez cette DataTemplatex:Key
valeur, vous substituez l’implicite x:Key
et le DataTemplate ne serait pas appliqué automatiquement.
Si vous liez un ContentControl à une collection d’objets Task
, il ContentControl n’utilise pas automatiquement les éléments ci-dessus DataTemplate . Cela est dû au fait que la liaison sur un ContentControl besoin d’informations supplémentaires permet de distinguer si vous souhaitez établir une liaison à une collection entière ou aux objets individuels. Si vous ContentControl effectuez le suivi de la sélection d’un ItemsControl type, vous pouvez définir la Path propriété de la ContentControl liaison sur «/
» pour indiquer que vous êtes intéressé par l’élément actif. Vous trouverez un exemple sur la page Effectuer une liaison à une collection et afficher des informations en fonction de la sélection. Dans le cas contraire, vous devez spécifier la DataTemplate valeur explicitement en définissant la ContentTemplate propriété.
La DataType propriété est particulièrement utile lorsque vous disposez CompositeCollection de différents types d’objets de données. Vous trouverez un exemple sur la page Implémenter une CompositeCollection.
Ajouter d’autres informations au DataTemplate
Actuellement, les données s’affichent avec les informations nécessaires, mais on peut sans problème aller plus loin. Nous allons améliorer la présentation en ajoutant un Border, un , et Gridcertains TextBlock éléments qui décrivent les données affichées.
<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>
La capture d’écran suivante montre l’opération ListBox modifiée DataTemplate:
Nous pouvons définir HorizontalContentAlignmentStretch la valeur sur la ListBox zone pour vous assurer que la largeur des éléments occupe l’espace entier :
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
Avec la HorizontalContentAlignment propriété définie sur Stretch, le ListBox résultat ressemble maintenant à ceci :
Utiliser DataTrigger pour appliquer des valeurs de propriété
La présentation actuelle n’indique pas si une Task
est une tâche privée ou une tâche professionnelle. N’oubliez pas que l’objet Task
a une propriété TaskType
de type TaskType
, qui est une énumération avec les valeurs Home
et Work
.
Dans l’exemple suivant, le DataTrigger jeu de l’élément BorderBrush nommé border
Yellow
si la TaskType
propriété est 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>
Notre application se présente maintenant ainsi. Les tâches privées apparaissent avec une bordure jaune et les tâches professionnelles avec une bordure cyan :
Dans cet exemple, l’exemple DataTrigger utilise un Setter pour définir une valeur de propriété. Les classes de déclencheur ont également les propriétés et ExitActions les EnterActions propriétés qui vous permettent de démarrer un ensemble d’actions telles que des animations. En outre, il existe également une MultiDataTrigger classe qui vous permet d’appliquer des modifications en fonction de plusieurs valeurs de propriété liées aux données.
Une autre façon d’obtenir le même effet consiste à lier la BorderBrush propriété à la TaskType
propriété et à utiliser un convertisseur de valeur pour retourner la couleur en fonction de la TaskType
valeur. Du point de vue des performances, il est légèrement plus efficace d’utiliser un convertisseur pour produire cet effet. En outre, le fait de créer votre propre convertisseur vous offre plus de souplesse, car vous fournissez votre propre logique. En somme, la technique choisie dépend du scénario et des préférences. Pour plus d’informations sur l’écriture d’un convertisseur, consultez IValueConverter.
Que contient un DataTemplate ?
Dans l’exemple précédent, nous avons placé le déclencheur dans la propriété à l’aide DataTemplate de la DataTemplate.Triggers propriété. Le Setter déclencheur définit la valeur d’une propriété d’un élément (l’élément Border ) qui se trouve dans le DataTemplate. Toutefois, si les propriétés que vous Setters
êtes concernées ne sont pas des propriétés d’éléments qui se trouvent dans le courant DataTemplate, il peut être plus approprié de définir les propriétés à l’aide d’une Style valeur correspondant à la ListBoxItem classe (si le contrôle que vous liez est un ListBox). Par exemple, si vous souhaitez animer la Opacity valeur de l’élément lorsqu’une souris pointe vers un élément, vous définissez Trigger des déclencheurs dans un ListBoxItem style. Vous trouverez un exemple sur la page Présentation d’un exemple de création de style et de modèle.
En règle générale, gardez à l’esprit que l’application DataTemplate est appliquée à chacun des éléments générés ListBoxItem (pour plus d’informations sur la façon et l’emplacement d’application, consultez la ItemTemplate page.). Votre DataTemplate préoccupation concerne uniquement la présentation et l’apparence des objets de données. Dans la plupart des cas, tous les autres aspects de la présentation, tels que l’apparence d’un élément lorsqu’il est sélectionné ou la façon dont les ListBox éléments sont exposés, n’appartiennent pas à la définition d’un DataTemplate. Vous trouverez un exemple dans la section Création de styles et de modèles pour un ItemsControl.
Choisir un DataTemplate en fonction des propriétés de l’objet de données
Dans la section La propriété DataType, nous avons expliqué que vous pouvez définir différents modèles de données pour différents objets de données. Cela est particulièrement utile lorsque vous avez un CompositeCollection type ou des collections différents avec des éléments de différents types. Dans la section Utiliser DataTriggers pour appliquer des valeurs de propriété, nous avons montré que si vous avez une collection du même type d’objets de données que vous pouvez créer, DataTemplate puis utilisez des déclencheurs pour appliquer des modifications en fonction des valeurs de propriété de chaque objet de données. Toutefois, si les déclencheurs vous permettent d’appliquer des valeurs de propriété ou de lancer des animations, ils ne vous donnent pas la possibilité de reconstruire la structure de vos objets de données. Certains scénarios peuvent nécessiter la création d’un autre DataTemplate type pour les objets de données qui sont de même type, mais qui ont des propriétés différentes.
Par exemple, lorsque un objet Task
a une valeur Priority
égale à 1
, vous avez la possibilité de lui donner un aspect différent qui vous servira d’alerte. Dans ce cas, vous créez un DataTemplate pour l’affichage des objets de haute priorité Task
. Ajoutons ce qui suit DataTemplate à la section ressources :
<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>
Cet exemple utilise la propriété DataTemplate.Resources . Les ressources définies dans cette section sont partagées par les éléments dans le DataTemplate.
Pour fournir une logique pour choisir laquelle DataTemplate utiliser en fonction de la Priority
valeur de l’objet de données, créez une sous-classe de DataTemplateSelector la méthode et remplacez la SelectTemplate méthode. Dans l’exemple suivant, la SelectTemplate méthode fournit une logique pour retourner le modèle approprié en fonction de la valeur de la Priority
propriété. Le modèle à retourner se trouve dans les ressources de l’élément enveling 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
On peut ensuite déclarer le TaskListDataTemplateSelector
comme ressource :
<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>
Pour utiliser la ressource de sélecteur de modèle, affectez-la à la ItemTemplateSelector propriété du ListBox. Appelle ListBox la SelectTemplate méthode de chacun TaskListDataTemplateSelector
des éléments de la collection sous-jacente. L’appel passe l’objet de données en paramètre d’élément. L’objet DataTemplate de données retourné par la méthode est ensuite appliqué à cet objet de données.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"/>
Avec le sélecteur de modèle en place, le ListBox présent apparaît comme suit :
Ainsi se conclut notre étude de cet exemple. Vous trouverez l’exemple complet sur la page Présentation d’un exemple de création de modèles de données.
Création de styles et de modèles pour un ItemsControl
Même si le ItemsControl type de contrôle n’est pas le seul avec lequel vous pouvez utiliser, DataTemplate il s’agit d’un scénario très courant pour lier un ItemsControl à une collection. Dans la section Qu’appartient un DataTemplate , nous avons discuté que la définition de vos DataTemplate données ne devrait être concernée que par la présentation des données. Pour savoir quand il n’est pas approprié d’utiliser un modèle DataTemplate , il est important de comprendre les différentes propriétés de style et de modèle fournies par le ItemsControl. L’exemple suivant est conçu pour illustrer la fonction de chacune de ces propriétés. L’exemple ItemsControl suivant est lié à la même Tasks
collection que dans l’exemple précédent. À des fins de démonstration, les styles et les modèles de cet exemple sont tous déclarés 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>
Voici une capture d’écran du rendu de l’exemple :
Notez qu’au lieu d’utiliser le ItemTemplate, vous pouvez utiliser le ItemTemplateSelector. Vous trouverez un exemple dans la section précédente. De même, au lieu d’utiliser le ItemContainerStyle, vous avez la possibilité d’utiliser le ItemContainerStyleSelector.
Deux autres propriétés liées au style de celles ItemsControl qui ne sont pas affichées ici sont GroupStyle et GroupStyleSelector.
Prise en charge des données hiérarchiques
Jusqu’à présent, nous avons seulement vu comment lier et afficher une collection unique. Une collection peut contenir d’autres collections. La HierarchicalDataTemplate classe est conçue pour être utilisée avec HeaderedItemsControl des types pour afficher ces données. Dans l’exemple suivant, ListLeagueList
est une liste d’objets League
. Chaque objet League
a un Name
et une collection d’objets Division
. Chaque Division
a un Name
et une collection d’objets Team
et chaque objet Team
a 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’exemple montre qu’avec l’utilisation de , vous pouvez facilement afficher des données de HierarchicalDataTemplateliste qui contiennent d’autres listes. Voici une capture d’écran de l’exemple.
Voir aussi
.NET Desktop feedback