Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le modèle de création de modèles de données WPF vous offre une grande flexibilité pour définir la présentation de vos données. Les contrôles WPF disposent de fonctionnalités intégrées pour prendre en charge la personnalisation de la présentation des données. Cette rubrique commence par montrer comment définir une DataTemplate, puis introduit d'autres fonctionnalités de création de modèles de données, telles que la sélection de modèles basée sur une logique personnalisée et la prise en charge de l'affichage des données hiérarchiques.
Conditions préalables
Cette rubrique se concentre sur les fonctionnalités de création de modèles de données et n’est pas une introduction des concepts de liaison de données. Pour plus d’informations sur les concepts de liaison de données de base, 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 Ressources XAML.
Concepts de base de 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 Task objet a une TaskName (chaîne), une Description (chaîne), un Priority (int) 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 un modèle de données
Sans un DataTemplate, voici à quoi ressemble notre ListBox actuellement.
Ce qui se passe, c'est que, sans instructions spécifiques, le ListBox appelle par défaut le ToString lorsque vous essayez d'afficher les objets dans la collection. Par conséquent, si l'objet Task remplace la méthode ToString, l'objet ListBox affiche la représentation sous forme de chaîne de chaque objet source dans la collection sous-jacente.
Par exemple, si la Task classe remplace la ToString méthode de cette façon, où name est le champ de la TaskName propriété :
public override string ToString()
{
return name.ToString();
}
Public Overrides Function ToString() As String
Return _name.ToString()
End Function
Ensuite, le ListBox ressemble à ceci :
Toutefois, cela limite et est inflexible. En outre, si vous effectuez une liaison à des données XML, vous ne pouvez pas remplacer ToString.
Définition d’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 XPath à @TaskName si TaskName est un attribut de votre nœud XML.
Maintenant, notre ListBox ressemble à ceci :
Création de DataTemplate en tant que ressource
Dans l’exemple ci-dessus, nous avons défini l’inline DataTemplate . Il est plus courant de le définir dans la section ressources afin qu’il puisse être 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 maintenant utiliser myTaskTemplate comme ressource, comme dans l’exemple suivant :
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
myTaskTemplate étant une ressource, vous pouvez désormais l'utiliser sur d'autres contrôles qui ont une propriété qui prend un type DataTemplate. 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é.
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 valeur est définie implicitement. Par conséquent, si vous affectez à cette DataTemplate une x:Key valeur, vous remplacez l'implicite x:Key et le DataTemplate n'est 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 a besoin d'informations supplémentaires pour distinguer si vous souhaitez établir une liaison à une collection entière ou aux objets individuels. Si votre ContentControl suit la sélection d’un ItemsControl type, vous pouvez définir la propriété Path de la liaison ContentControl sur «/» pour indiquer que vous êtes intéressé par l’élément actuel. Pour obtenir un exemple, consultez Lier à une collection et afficher des informations basées sur 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. Pour obtenir un exemple, consultez Implémenter un CompositeCollection.
Ajout d’autres éléments au DataTemplate
Actuellement, les données apparaissent avec les informations nécessaires, mais il est certainement possible d’améliorer. 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 le ListBox avec ce DataTemplate modifié :
Nous pouvons définir HorizontalContentAlignment à Stretch sur ListBox pour s'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 apparaît maintenant comme ceci :
Utiliser DataTriggers pour appliquer des valeurs de propriété
La présentation actuelle ne nous dit pas si un Task est une tâche à domicile ou une tâche de bureau. N’oubliez pas que l’objet Task a une TaskType propriété de type TaskType, qui est une énumération avec des valeurs Home et Work.
Dans l’exemple suivant, le DataTrigger définit le BorderBrush de l’élément nommé border à Yellow si la propriété TaskType 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 ressemble maintenant à ce qui suit. Les tâches d’accueil apparaissent avec une bordure jaune et les tâches de bureau apparaissent avec une bordure aqua :
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 EnterActions et ExitActions 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. La création de l’effet ci-dessus à l’aide d’un convertisseur est légèrement plus efficace en termes de performances. En outre, la création de votre propre convertisseur vous donne plus de flexibilité, car vous fournissez votre propre logique. En fin de compte, la technique que vous choisissez dépend de votre scénario et de votre préférence. Pour plus d’informations sur l’écriture d’un convertisseur, consultez IValueConverter.
Qu’appartient-il à un DataTemplate ?
Dans l'exemple précédent, nous avons placé le déclencheur dans le DataTemplate à l'aide de la propriété DataTemplate.Triggers. 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 qui vous préoccupent Setters ne sont pas des propriétés d’éléments présents dans l'élément actuel DataTemplate, il peut être plus approprié de définir les propriétés à l'aide d'un Style spécifique à la classe ListBoxItem (si le contrôle que vous liez est un ListBox). Par exemple, si vous souhaitez que votre Trigger anime la Opacity valeur de l’élément lorsqu’une souris pointe vers un élément, vous définissez des déclencheurs dans un ListBoxItem style. Pour un exemple, consultez l'Introduction aux styles et à la création de modèles.
En règle générale, gardez à l’esprit que DataTemplate est appliqué à chacun des ListBoxItem générés (pour plus d’informations sur la façon et le lieu d’application, consultez la page ItemTemplate). 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. Pour obtenir un exemple, consultez la section Stylisation et modélisation d'un ItemsControl.
Choix d’un DataTemplate basé sur les propriétés de l’objet de données
Dans la section Propriété DataType , nous avons discuté 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 de différents types ou des collections avec des éléments de types variés. Dans la section Utiliser les DataTriggers pour appliquer des valeurs de propriété, nous avons montré que si vous avez une collection du même type d'objets de type données, vous pouvez créer un DataTemplate et ensuite utiliser des déclencheurs pour appliquer des modifications en fonction des valeurs de propriété de chaque objet de données. Toutefois, les déclencheurs vous permettent d’appliquer des valeurs de propriété ou de démarrer des animations, mais 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, lorsqu'un Task objet a une Priority valeur de 1, vous pouvez lui donner une apparence complètement différente pour servir d'alerte pour vous. 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 permettant de choisir quel DataTemplate utiliser en fonction de la valeur de Priority de l'objet de données, créez une sous-classe de DataTemplateSelector et remplacez la méthode SelectTemplate. 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 enveloppant 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
Nous pouvons ensuite déclarer TaskListDataTemplateSelector comme une 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.
ListBox appelle la méthode SelectTemplate de TaskListDataTemplateSelector pour chacun des éléments de la collection sous-jacente. L’appel transmet l’objet de données en tant que paramètre d’élément. Le DataTemplate 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 :
Cela conclut notre discussion sur cet exemple. Pour obtenir l’exemple complet, consultez Introduction à l’exemple de création de modèles de données.
Mise en forme et création de modèles d'un ItemsControl
Même si le ItemsControl n'est pas le seul type de contrôle avec lequel vous pouvez utiliser DataTemplate, il est très courant de lier un ItemsControl à une collection. Dans la section Qu'est-ce qui appartient à un DataTemplate, nous avons discuté que la définition de votre DataTemplate ne devrait s'occuper que de 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 de l’exemple lorsqu’il est rendu :
Notez qu’au lieu d’utiliser le ItemTemplate, vous pouvez utiliser le ItemTemplateSelector. Reportez-vous à la section précédente pour obtenir un exemple. 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 n’avons examiné comment lier et afficher qu’une collection unique. Parfois, vous disposez d’une collection qui contient d’autres collections. La classe HierarchicalDataTemplate est conçue pour être utilisée avec les types HeaderedItemsControl afin d'afficher ces données. Dans l’exemple suivant, ListLeagueList il s’agit d’une liste d’objets League . Chaque League objet a un Name et une collection d’objets Division. Chacun des 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 HierarchicalDataTemplate, vous pouvez facilement afficher des données de liste qui contiennent d’autres listes. Voici une capture d’écran de l’exemple.
Voir aussi
.NET Desktop feedback