Après ce titre alléchant, une petite déception: la version actuelle de WPF ne permet pas de modifier un DataTemplate déclaré en Xaml depuis le code-behind. Deux workarounds simples sont communément utilisés:
Ces deux approches sont à la fois peu élégantes et fastidieuses. Dans le premier cas il faudra créer des DataTemplates from scratch et couvrir tous les cas possibles. Dans le second cas il vous faudra manier les nombreux appels de méthodes et imbrications complexes que le Xaml nous épargne, et nous coupe également toute possibilité d'utilisation d'Expression Blend.
Je vais vous présenter une troisième manière de procéder. Celle-ci consiste en la déclaration en Xaml d'un DataTemplate « source » pouvant être modifié, généré puis appliqué à la volée. Le projet exemple comporte une classe Personne, le DataTemplate d'origine, et une fenêtre permettant de modifier les propriétés affichées par le DataTemplate en fonction de l’état de trois CheckBoxes.
La déclaration du DataTemplate doit se faire dans un fichier à part, qui sera inclus dans le projet en tant que ressource. Il est important de noter que ce fichier étant défini avec une Build Action de Resource, il ne sera pas compilé en Baml, car il sera interprété par le programme uniquement au runtime.
Le fichier Xaml reste éditable depuis Expression Blend, mais il faudra faire attention à nommer ses éléments critiques à la manière des lookless controls WPF (PART_xxx). Il faudra également inclure tous les namespaces utilisés par le DataTemplate. En effet bien que le fichier soit inclus dans le projet, celui-ci aura besoin de résoudre toutes ses dépendances au runtime.
xmlns:local="clr-namespace:RuntimeDataTemplates;assembly=RuntimeDataTemplates"…<TextBlock Name="PART_Nom" Margin="5,2,5,2" Text="{Binding Path=Nom}"/>
Dans le code-behind, on accèdera au contenu de ce fichier comme à n’importe quelle ressource:
Uri uri = new Uri("/PersonneBaseDataTemplate.xaml", UriKind.Relative);StreamResourceInfo info = Application.GetResourceStream(uri);
Le fichier ressource étant du Xaml, donc du Xml, utilisons LINQ to XML pour accéder plus rapidement à ses éléments. Si la CheckBox ChkHorizOrient est cochée, le code suivant recherchera un élément nommé PART_panel, et modifiera son attribut Orientation pour qu’il prenne la valeur Horizontal.
#region Si nécessaire, modification du StackPanel
if (chkHorizOrient.IsChecked == true)
{ // Prend les panel se nommant "pnl" var panel = (from xe in sourceXDoc.Descendants() where (string)xe.Attribute("Name") == "PART_StackPanel" select xe).First();
// Puis ajoute l'attribut Orientation="Horizontal" au premier // (il ne doit y en avoir qu'un de toute maniŠre..)
Une fois le Xaml modifié, reste à l’appliquer à la fenêtre. Pour cela, il faut transformer le XDocument en Stream, que l’on chargera par le biais de XamlReader.Load().
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(sourceXDoc.ToString());
MemoryStream sXamlModifie = new MemoryStream(bytes);
DataTemplate dt = XamlReader.Load(sXamlModifie) as DataTemplate;
lbx.ItemTemplate = dt; // Application du template
En résumé, la méthode proposée ici permet de pallier partiellement à l’impossibilité de modifier les DataTemplates déclarés en Xaml au runtime, en incluant ce code en tant que ressource qui sera chargé à la demande. Ses avantages principaux sont
… mais ses inconvénients sont: