Dans la première partie de cette série de posts nous avons vu comment définir nous même les colonnes d’un DataGrid en utilisant le DataGridTextColumn pour définir une colonne de type texte et le DataGridCheckBoxColumn pour définir une colonne de type CheckBox. Dans ce post nous allons voir comment utiliser DataGridTemplateColumn pour créer des colonnes complètement personnalisés.
Reprenons l’exemple que nous avons initié dans ce post et que nous avons enrichi dans celui-ci. Voici pour rappel le xaml du DataGrid
<Data:DataGrid x:Name="TasksGrid" AutoGenerateColumns="False" Style="{StaticResource MyGridStyle}" ItemsSource="{Binding Tasks}">
<Data:DataGrid.Columns>
<Data:DataGridTextColumn Binding="{Binding Name}" Header="Nom"></Data:DataGridTextColumn>
<Data:DataGridCheckBoxColumn Binding="{Binding Realized}" Header="Réalisé"></Data:DataGridCheckBoxColumn>
</Data:DataGrid.Columns>
</Data:DataGrid>
Et voici le résultat :
Nous allons maintenant rajouter deux colonnes à notre grille :
- La première contenant un voyant qui sera rouge si la tâche n’est pas encore réalisée et qui sera vert si la tâche est réalisée.
- La deuxième colonne va contenir un bouton qui permettra par la suite d’ouvrir une popup (Child window) pour visualiser la tâche
Pour ces deux colonnes nous allons utiliser DataGridTemplateColumn. C’est un type de colonne complètement personnalisable et qui, par défaut, possède un modèle vide. Nous pouvons alors y placer tous les contrôles que nous voulons, ces contrôles vont alors constituer notre colonne et se répéter pour chaque ligne liée à partir de la source de données.
Côté xaml nous allons utiliser la balise DataGridTemplateColumn dans la quelle nous pouvons définir entre autre le CellTemplate qui va contenir le modèle de cellule pour cette colonne en mode lecture. L’EditTemplate quand à lui peut être définit au même niveau que le CellTemplate et va contenir le modèle de cellule pour cette colonne lorsqu'elle sera en mode édition. Voici la définition d’une DataGridTemplateColumn avec un Cell Template et un EditTemplate vides.
<Data:DataGridTemplateColumn Header="" IsReadOnly="True">
<Data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
</DataTemplate>
</Data:DataGridTemplateColumn.CellEditingTemplate>
<Data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
</DataTemplate>
</Data:DataGridTemplateColumn.CellTemplate>
</Data:DataGridTemplateColumn>
Les templates vides comme définis ci-dessous lèvent une exception à l’exécution, il faut placer au moins un contrôle dans chaque DataTemplate.
La colonne voyant de réalisation
Nous allons tout d’abord enrichir notre classe Task en ajoutant une propriété que nous allons appeler RealizedColor, cette propriété va nous retourner la couleur verte si la valeur de la propriété Realized est à true et la couleur rouge le cas échéant.
public SolidColorBrush RealizedColor
{
get
{
if (Realized)
return new SolidColorBrush(Colors.Green);
else
return new SolidColorBrush(Colors.Red);
}
}
Il suffit maintenant de rajouter une ellipse à notre template et de binder sa propriété Fill avec la propriété RealizedColor de notre classe task.
Positionnez vous en dessous de ces lignes et avant la fermeture de la balise DataGrid.Columns
<Data:DataGrid.Columns>
<Data:DataGridTextColumn Binding="{Binding Name}" Header="Nom"></Data:DataGridTextColumn>
<Data:DataGridCheckBoxColumn Binding="{Binding Realized}" Header="Réalisé"></Data:DataGridCheckBoxColumn>
et insérez le xaml suivant
<Data:DataGridTemplateColumn Header="" IsReadOnly="True">
<Data:DataGridTemplateColumn.CellTemplate>
<DataTemplate x:Name="TemplateRealized">
<Ellipse Fill="{Binding RealizedColor}" Width="15" Height="15"></Ellipse>
</DataTemplate>
</Data:DataGridTemplateColumn.CellTemplate>
</Data:DataGridTemplateColumn>
La colonne bouton de visualisation
Même principe pour cette colonne, sauf que nous allons juste rajouter un bouton, nous n’allons pas définir son fonctionnement pour un souci de simplification. Voici le xaml à insérer juste après celui que nous nous venons d’insérer :
<Data:DataGridTemplateColumn Header="" IsReadOnly="True">
<Data:DataGridTemplateColumn.CellTemplate>
<DataTemplate x:Name="TemplateVisualiser">
<Button x:Name="btnVisualiser" Content="Visualiser"></Button>
</DataTemplate>
</Data:DataGridTemplateColumn.CellTemplate>
</Data:DataGridTemplateColumn>
En exécutant, nous devrions avoir quelque chose qui ressemble à ça

Il reste encore le bouton visualiser qui ne fonctionne pas encore et pour cause nous n’avons pas défini l’EventHandler correspondant à l’évènement click du bouton. Pour cela, placez vous dans la balise bouton et commencez à taper le mot “click” l’IntelliSense va vous proposer l’évènement click, appuyez alors sur “=” et visual studio va vous proposer de créer pour vous l’EventHandler pour cet évènement, appuyez sur Entrée.
Si vous allez dans votre code behind vous allez trouver la méthode créée qui s’appelle btnVisualiser_Click (si vous avez nommé votre Bouton comme moi). Dans cette méthode nous allons instancier une ChildWindow, affecter l’objet Task à son DataContext et ouvrir la ChildWindow qui va afficher le contenu de l’objet Task contenu dans le DataContext.
Nous allons maintenant créer la ChildWindow qui va contenir le détail de la ligne. Positionnez vous au niveau du répertoire Views, click droit sur Views –> Add –> New Item –> Silverlight Child Window et nommez la Detail.xaml.
Pour afficher les données nous allons utiliser un DataForm (Composant du Silverlight Toolkit), voici tout le code xaml de la ChildWindow
<controls:ChildWindow xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit" x:Class="TasksManager.View.Views.Detail"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Width="400" Height="300"
Title="Detail">
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<dataFormToolkit:DataForm Grid.Row="0" AutoGenerateFields="True" AutoCommit="False" AutoEdit="False" x:Name="dataForm">
</dataFormToolkit:DataForm>
</Grid>
</controls:ChildWindow>
Dans le code behind de Detail.xaml, nous allons nous abonner à l’évènement “Loaded” et dans la méthode “Detail_Loaded” qui va être déclenchée après le chargement de la ChildWindow, nous allons remplir l’ItemSource de notre DataForm à l’aide du DataContext. Voici le code Behind de la Child Window :
public partial class Detail : ChildWindow
{
public Detail()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Detail_Loaded);
}
void Detail_Loaded(object sender, RoutedEventArgs e)
{
List<Task> tasks = new List<Task>();
tasks.Add(this.DataContext as Task);
dataForm.ItemsSource = tasks;
}
}
Maintenant si vous exécutez votre code vous devriez voir les deux colonnes que nous avons rajouté
et si vous cliquez sur visualiser vous allez voir apparaître votre ChildWindow avec le détail de la ligne en cours.
L’utilisation du DataForm n’est pas vraiment appropriée ici puisque ce composant est fait pour gérer des collections d’objets mais pour les besoins de la démo cela suffit. Bien entendu, nous pouvons toujours aller plus loin en définissant des TemplateField pour le DataForm.
Voila voila, les TemplateColumn ne devraient plus avoir de secret pour vous au même titre que les ChildWindow dont l’utilisation se révèle très utile et surtout très simple. Dans le prochain post nous allons voir comment utiliser les Converter pour encore plus loin dans la personnalisation de votre DataGrid.