[SL5] : Silverlight 5 Implicit DataTemplate

Très connu en WPF, la notion de DataTemplate implicites est nouvelle en Silverlight 5. Afin de comprendre la puissance et la simplicité de codage que cela apporte nous allons réaliser deux exemples représentatifs de son utilisation.

Cas d’utilisation pour une composition d’IHM

Ce mode là est souvent utilisé en WPF pour réaliser une composition de l’interface via le patter ViewModelFirst qui consiste à substituer une donnée (ex classe) par une vue spécifique.

Pour illustrer cela nous allons réaliser 3 classes basiques ClassA, ClassB et ClassC et une classe GlobalViewModel contenant 3 propriétées PropertyA de type ClassA, PropertyB de type ClassB et PropertyC de type ClassC.

Les classes

 public class ClassA
    {
    }

    public class ClassB
    {
    }

    public class ClassC
    {
    }

    public class GlobalViewModel
    {
        public GlobalViewModel()
        {
            PropertyA = new ClassA();
            PropertyB = new ClassB();
            PropertyC = new ClassC();
        }
        public ClassA PropertyA
        {
            get;
            set;
        }

        public ClassB PropertyB
        {
            get;
            set;
        }

        public ClassC PropertyC
        {
            get;
            set;
        }
    }

Controles Utilisateurs

Nous allons réaliser 3 UserControls UC1, UC2 et UC3 correspondants respectivement aux classes ClassA, ClassB et ClassC.

UC1
<UserControl x:Class="Silverlight5Tests.UC1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid Background="GreenYellow">
        <TextBlock Text="A"
                   FontSize="150"
                   FontWeight="Bold"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center" />

    </Grid>
</UserControl>
UC2
<UserControl x:Class="Silverlight5Tests.UC2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid Background="DarkCyan">
        <TextBlock Text="B"
                   FontSize="150"
                   FontWeight="Bold"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center" />

    </Grid>
</UserControl>

UC3
<UserControl x:Class="Silverlight5Tests.UC3"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid Background="BlanchedAlmond">
        <TextBlock Text="C"
                   FontSize="150"
                   FontWeight="Bold"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center" />

    </Grid>
</UserControl>

La vue principale

Ensuite nous allons créer une vue principale dans laquelle nous souhaitons afficher les 3 contrôles de façon automatique

<navigation:Page x:Class="Silverlight5Tests.DataTypeTest"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
                 xmlns:local="clr-namespace:Silverlight5Tests"
                 Title="DataType Page">
    <navigation:Page.Resources>
        <DataTemplate DataType="local:ClassA">
            <local:UC1 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassB">
            <local:UC2 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassC">
            <local:UC3 />
        </DataTemplate>
    </navigation:Page.Resources>

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--UC1-->
        <ContentControl Content="{Binding PropertyA}"
                        HorizontalContentAlignment="Stretch"
                        VerticalContentAlignment="Stretch" />
        <!--UC2-->
        <ContentControl Content="{Binding PropertyB}"
                        HorizontalContentAlignment="Stretch"
                        VerticalContentAlignment="Stretch"
                        Grid.Column="1" />
        <!--UC3-->
        <ContentControl Content="{Binding PropertyC}"
                        HorizontalContentAlignment="Stretch"
                        VerticalContentAlignment="Stretch"
                        Grid.Row="1"
                        Grid.ColumnSpan="2" />

    </Grid>
</navigation:Page>

Puis la magie s’effectue grâce a la nouvelle propriété DataType héritée de ce que nous connaissons en WPF. Vous pouvez mettre cela en ressource locale ou tout simplement en ressource globale à l’application si vous en avez besoin à d’autres endroits.
Nous réalisons 3 DataTemplate représentants nos 3 classes avec leurs UserControls associés.

  <navigation:Page.Resources>
        <DataTemplate DataType="local:ClassA">
            <local:UC1 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassB">
            <local:UC2 />
        </DataTemplate>
        <DataTemplate DataType="local:ClassC">
            <local:UC3 />
        </DataTemplate>
    </navigation:Page.Resources>

Résultat

image

L’intérêt de cette solution est de s’abstraire le plus possible et de ne pas avoir à référencer un UserControl à chaque fois qu’on en a besoin. Ainsi le changement du DataTemplate provoquera automatiquement le changement du comportement de votre application sans avoir à modifier l’ensemble de l’application.

Cas d’une liste hétérogène

Prenons le cas d’un liste de User contenant quelques champs comme le nom, le prénom et une ville. Nous disposons aussi de deux autres classes dérivées nommées NormalUser et SuperUser.

Grace au DataTemplate explicites nous pouvons réaliser une affichage différent pour chaque types de User. Tout cela en quelques lignes de XAML…

Notre modèle de données


    public abstract class UserBase
    {
        public String LastName
        {
            get;
            set;
        }

        public String FirstName
        {
            get;
            set;
        }

        public String Town
        {
            get;
            set;
        }
    }

    public class NormalUser : UserBase
    {
    }

    public class SuperUser : UserBase
    {
        public String SampleProperty
        {
            get;
            set;
        }
    }

UserControl d’un utilisateur normal

<UserControl x:Class="Silverlight5Tests.UserItemView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Border BorderBrush="#FF8D8D8D" BorderThickness="0,0,0,1" Background="#FFCDD7FF">

		<StackPanel Margin="5">
			<TextBlock Text="{Binding LastName}" />
			<TextBlock Text="{Binding FirstName}" />
		</StackPanel>
	</Border>
</UserControl>

UserControl d’un super utilisateur

<UserControl x:Class="Silverlight5Tests.SuperUserItemView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Border BorderThickness="0,0,0,1" Background="#FFFFB396" BorderBrush="#FF8D8D8D">

		<StackPanel Margin="5">
			<TextBlock Text="{Binding LastName}" />
			<TextBlock Text="{Binding FirstName}" />
		</StackPanel>
	</Border>
</UserControl>

Affichage de la liste des utilisateurs

Pour cela il suffit de déclarer les DataTemplates explicites correspondant aux classes NormalUser et SuperUser.

XAML
<navigation:Page x:Class="Silverlight5Tests.ImplicitTemplates"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
                 xmlns:local="clr-namespace:Silverlight5Tests"
                 Title="ImplicitTemplates Page">
    <Grid x:Name="LayoutRoot">
        <ItemsControl ItemsSource="{Binding}">
            <ItemsControl.Resources>
                <DataTemplate DataType="local:NormalUser">
                    <local:UserItemView />
                </DataTemplate>
                <DataTemplate DataType="local:SuperUser">
                    <local:SuperUserItemView />
                </DataTemplate>
            </ItemsControl.Resources>
        </ItemsControl>
    </Grid>
</navigation:Page>

CS
using System;
using System.Collections.Generic;
using System.Windows.Controls;

namespace Silverlight5Tests
{
    public partial class ImplicitTemplates : Page
    {
        public ImplicitTemplates()
        {
            InitializeComponent();

            DataContext = new List()
            {
                new NormalUser()
                {
                    LastName = "LOUGE",
                    FirstName = "Stéphane",
                    Town = "Biarritz"
                },
                 new SuperUser()
                {
                    LastName = "GATES",
                    FirstName = "Bill",
                    Town = "Seattle"
                },
                 new NormalUser()
                {
                    LastName = "HULOT",
                    FirstName = "Nicolas",
                    Town = "Paris"
                },
                new NormalUser()
                {
                    LastName = "VIGIE",
                    FirstName = "Julie",
                    Town = "Iron"
                }
            };
        }
    }

}

Resultat

Silverlight5ImplicitDataTemplate

3 commentaires sur “[SL5] : Silverlight 5 Implicit DataTemplate

Ajouter un commentaire

  1. Bonjour Stéphane,

    Merci pour ce très bon tutoriel.

    Dans le premier exemple : « Cas d’utilisation pour une composition d’IHM ».
    Je pense que quelque chose manque. En effet, tu écris :

    Pour que le DataTemplate soit appliqué automatiquement a tous les object de type ClassA, il aurait fallu écrire :

    ………..

    Autrement, il faut fournir explicitement la clé du DataTemplate de la façon suivante :

    Ce qui le rend moins déjà moins implicite.

    Bonne Continuation.

    Laerian.

  2. Re-Bonjour Stéphane,

    (désolé petite faute de frappe 🙂

    Dans le premier exemple : « Cas d’utilisation pour une composition d’IHM ».
    Je pense que quelque chose manque. En effet, tu écris :

    DataTemplate DataType= »local:ClassA »

    Pour que le DataTemplate soit appliqué automatiquement a tous les object de type ClassA, il aurait fallu écrire :

    DataTemplate DataType= »{x:Type local:ClassA} »

    Autrement, il faut fournir explicitement la clé du DataTemplate de la façon suivante :

    DataTemplate DataType= »local:ClassA »
    local:UC1
    DataTemplate

    ContentControl Content= »{Binding PropertyA} »
    HorizontalContentAlignment= »Stretch »
    VerticalContentAlignment= »Stretch »
    ContentControl.ContentTemplate= »{StaticResource TemplateA} »

    Ce qui le rend moins déjà moins implicite.

    Bonne Continuation.

    Laerian.

  3. Bonjour Laerian, merci pour ton commentaire tout à fait judicieux 🙂
    En effet j’aurai du parler de comment on aurait fait sans cette technique. Je vais faire une mise à jour de l’article dans la semaine afin de montrer un autre concept.
    Concernant le {x:Type local:ClassA} il n’est pas possible pour le moment l’utiliser tel quel en SL5 car le compilateur ne le supporte pas encore (la syntaxe est donc uniquement valable en WPF pour le moment). Il faut donc pour l’instant écrire local:ClassA.
    Je remettrai un petit commentaire quand je l’aurai mis à jour.

    Bonne journée 🙂
    Stéphane

Laisser un commentaire

Un site Web propulsé par WordPress.com.

Retour en haut ↑