Personnaliser les colonnes d'un DataGrid en Silverlight : 3ème partie, utilisation du ValueConverter#

Dans les deux première parties de cette série d’articles nous avons utilisé une DataGrid pour afficher des données provenant d’un service WCF en utilisant l’option de génération automatique de colonnes. Ensuite nous avons personnalisé ces colonnes dans un premier temps à l’aide du DataGridTextColumn et du DataGridCheckBoxColumn, et dans un deuxième temps avec le DataGridTemplateColumn. Voici le résultat obtenu :

Nous allons maintenant ajouter l’affichage de la date de réalisation de la tâche mais comme la date est de type DateTime lorsqu’on va la binder dans une colonne de type Text, nous allons voir la date et heure complète alors que ce qui nous intéresse c’est uniquement la date. Dans cet article nous allons donc utiliser un ValueConverter pour afficher la date au format attendu.

Pour suivre cet exemple vous pouvez télécharger les sources joints à cet article.

Commençons d’abord par rajouter l’affichage de la date, dans le projet TasksManager.View (l’application Silverlight), répertoire Model, ouvrez la classe Task et rajouter la propriété date de réalisation comme suit :

public DateTime? RealizationDate { get; set; }

Dans le même projet, le répertoire ViewModel ouvrez la classe TasksViewModel et localisez la méthode taskServiceClient_GetListCompleted et rajoutez la ligne d’affectation de la date de réalisation comme suit :

void taskServiceClient_GetListCompleted(object sender, TasksManager.View.TasksService.GetListCompletedEventArgs e)
{
       List<Task> resultTasks = new List<Task>();
       foreach (TasksService.Task t in e.Result)
       {
           Task mt = new Task();
           mt.Id = t.id;
           mt.Name = t.name;
           mt.RealizationDate = t.realizationDate;
           if (t.realized.HasValue)
               mt.Realized = t.realized.Value;
           resultTasks.Add(mt);
       }
       this.Tasks = resultTasks;
}

Et enfin, dans le même projet, répertoire Views, ouvrez le fichier Tasks.xaml et dans <Data:DataGrid.Columns>, rajoutez la colonne suivante :

<Data:DataGridTextColumn Binding="{Binding RealizationDate}" Header="Date de réalisation"></Data:DataGridTextColumn>

Lancez maintenant le projet, voici ce que vous devriez obtenir cela :

image

La date de réalisation s’affiche pour les tâches pour les quelles la date est renseignée.

Nous voyons bien, comme prévu, que la date d’affiche avec un format long. Ceci parce que le ToString() d’une date convertit la date en chaîne de caractère avec ce format. Nous allons maintenant mettre en place un converter pour afficher uniquement la date.

Dans le projet TasksManager.View, répertoire Views, ajoutez un nouveau répertoire et appelez le “Converters”. Dans ce répertoire ajoutez une nouvelle classe et nommez la “ShortDateFormatConverter”. Ensuite, implémentez l’interface IValueConverter en ajoutant l’espace de noms System.Windows.Data

image

Pour implémenter l’interface IValueConverter nous devons définir deux méthodes :

- La méthode Convert : qui convertit l’objet à binder vers l’objet que nous voulons afficher. Dans notre cas cette méthode prend en paramètre la date de réalisation de type DateTime et nous allons faire en sorte qu’elle renvoie une chaine de caractère représentant la date au format court.

- La méthode ConvertBack : qui fait le chemin inverse, c’est à dire qu’elle prend en paramètre la chaine de caractère affichée et représentant la date au format court pour la convertir au format d’origine (dans notre cas DateTime). Cette méthode est utile lorsqu’on met en place un Binding TwoWay, mais dans notre cas nous n’allons pas l’implémenter parce que notre but est uniquement l’affichage.

Voici le code de la classe ShortDateFormatConverter

public class ShortDateFormatConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        DateTime? date = value as DateTime?;

        if (date != null)
            return date.Value.ToShortDateString();

        return date;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Pour utiliser ce converter dans notre vue nous devons tout d’abord l’enregistrer comme ressource dans notre UserControl, ouvrez le fichier Tasks.xaml et placez vous juste après la déclaration du UserControl

<UserControl.Resources>
    <Converters:ShortDateFormatConverter x:Key="ShortDateFormatter"></Converters:ShortDateFormatConverter>
</UserControl.Resources>

Et enfin remplacez la ligne que nous avons ajouté au tout début de cet article

<Data:DataGridTextColumn Binding="{Binding RealizationDate}" Header="Date de réalisation"></Data:DataGridTextColumn>

Par celle – ci:

<Data:DataGridTextColumn Binding="{Binding RealizationDate, Converter={StaticResource ShortDateFormatter}}" Header="Date de réalisation"></Data:DataGridTextColumn>

Lancez maintenant votre application, vous devriez avoir ce résultat :

image

Tuesday, March 30, 2010 9:52:35 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

SqlFunctions dans Entity Framework 4#

Dans les versions précédentes d’Entity Framework, faire un calcul de différence entre deux dates avec linq to entities était assez laborieux, les fonctions c# n’étaient pas prise en compte dans les requêtes linq to entities et nous étions obligé soit de passer par une colonne calculée au sein de la Base de données soit modifier les fichiers de mapping pour écrire la requête sql à la main et utiliser la fonction DATEDIFF de SQL Server.

Avec Entity Framework 4 et l’apparition de la classe SqlFunctions, tout ça c’est de l’histoire ancienne. Cette classe contient un bon nombre de fonctions Sql Server qui peuvent être utilisées directement dans nos requêtes Linq To Entities et qui vont être traduites vers les fonctions SQL dans un contexte de requête à la BDD. Ces fonctions sont exposées directement par SQL Server et sont donc propres au Provider Sql Server. Parmis ces fonctions nous trouvons le DateDiff qui va nous permettre de calculer la différence entre 2 dates.

image

Soit cette requête linq to entites :

var requete = from s in context.Person
            select new { Nom = s.FirstName + " " + s.LastName, 
                                 s.BirthDate, 
                                 Age = System.Data.Objects.SqlClient.SqlFunctions.DateDiff("year", s.BirthDate, DateTime.Now) 
};

Du côté de profiler, voici la requête générée :

image

Nous remarquons que notre fonction DATEDIFF a été traduite littéralement.

Faisons maintenant un petit tour sur Reflector, voici ce que nous pouvons trouver

image

En effet, cette fonction est une fonction Sql Server mappée tout simplement. Donc en ce qui concerne les SqlFunctions, il vaut mieux se référer directement à la documentation Transact – SQL.

Friday, March 19, 2010 10:29:31 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Retour sur la session que j’ai co-animé avec Arnaud AUROUX aux Techdays 2010#

Comme prévu, j’ai animé hier (le lundi 08/02/2010) en compagnie d’Arnaud la session “Silverlight 4 : Cas pratique du mode déconnecté” aux Techdays. La session c’est très bien passée et nous remercions toutes les personnes qui se sont déplacés pour y assister. Malheureusement il y a des personnes qui n’ont pas pu accéder à la salle pour assister à la présentation faute de place, je m’excuse auprès d’eux et pour me racheter je publierais très prochainement la présentation ainsi que les sources de la démo. Bien évidemment, vous aurez aussi le web cast sur le site des Techdays bientôt, pour l’instant il n’y a pas encore de date précise mais je ne manquerais pas de vous informer dès que j’aurais l’info.

Pour ceux qui étaient parmi nous hier, j’aimerais avoir vos retours pour savoir si tout était clair? est ce que ça vous a permis d’acquérir de nouvelles connaissances? Est ce que ça correspondait à vos attentes? Ce qui vous a plu, ce qui était dommage, …

merci et peut être à très bientôt ;)

Une petite photo de l’équipe après la session …

DSC_0127 Arnaud sur la gauche de la photo et moi-même à droite

Wednesday, February 10, 2010 1:18:58 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Création de Custom control avec Silverlight#

Dans un souci de capitalisation ou encore d’industrialisation, nous avons souvent besoin de développer des custom control réutilisables. Dans ce post nous allons voir comment :

- Créer un custom control simple

- Le tester dans une application Silverlight

- Le personnaliser

Création du custom control

Nous allons maintenant créer un custom control, pour ce faire nous allons tout d’abord créer un nouveau projet de type “Silverlight class library” et le nommer “MyCircleControl”. Dans ce projet, renommez le fichier class1.cs à MyCircle.cs et répondez “oui” quand visual studio vous demande si vous voulez faire un refactor sur la classe. Ouvrez le fichier MyCircle.cs et faites hériter la classe MonCustomControl de control. Dans ce fichier, nous allons définir la logique du contrôle et les propriétés qu’il va exposer à ces utilisateurs.

Créez ensuite un répertoire nommé Themes à la racine du projet et créez dedans un fichier texte et nommez le generic.xaml. Dans ce fichier, nous allons pouvoir définir nos ressources à savoir les styles ou encore les valeurs pour les propriétés de notre contrôle. Une propriété intéressante à définir est la propriété “Template” que la classe MonCustomControl a hérité de la classe Control et qui va nous permettre de définir l’aspect visuel du contrôle. Voici la solution visual studio que vous devriez avoir.

image

Click droit sur le fichier generic.xaml et cliquer sur “Propriétés”, modifier les deux propriétés suivantes :

- Effacer le contenu de la propriété “Custom Tool”

- Modifier la propriété “Build Action” à “Resource”

Pour l’instant nous allons créer un custom control qui contiendra un cercle de couleur bleu.

Pour cela, ouvrez le fichier generic.xaml et placez y le code suivant :

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                    xmlns:custom="clr-namespace:MyCircleControl;assembly=MyCircleControl" 
                    >
    <Style TargetType="custom:MyCircle">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="custom:MyCircle">
                    <Grid>
                        <Ellipse x:Name="ACircle" Width="100" Height="100" Stroke="Blue"></Ellipse>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Quelques explications s’imposent :

La balise ResourceDictionary indique que ce fichier est un dictionnaire de ressources. Nous définissons un style avec la balise “Style” qui va s’appliquer à notre contrôle MyCircle. Dans cette balise nous allons définir le contenu visuel de notre contrôle, pour cela nous allons définir la propriété Template que notre contrôle a hérité de “Control” et dessiner un cercle de couleur bleu.

Testons le contrôle

Pour tester notre contrôle, nous allons créer un projet de type “Silverlight Application” dans la même solution, nommez le “MyCircleApplication”. Lorsque Visual studio vous demande si vous voulez créer un projet web pour héberger l’application Silverlight cliquez sur “OK” sans faire de changements

image

Vous devriez maintenant avoir une solution qui ressemble à ça :

image

Il  ne nous reste plus qu’à :

- Ajouter la référence vers le projet MyCircleCOntrol dans le projet “MyCircleApplication”

- Ouvrez le fichier “MainPage.xaml” et inclure le namespace de mon contrôle en rajoutant cette propriété à la balise “UserControl”

xmlns:circle="clr-namespace:MyCircleControl;assembly=MyCircleControl" 

Puis dans la balise “Grid” nous allons ajouter notre contrôle de la manière suivante :

<Grid x:Name="LayoutRoot">
      <circle:MyCircle></circle:MyCircle>
</Grid>

Compilez maintenant la solution et lancez la page MyCircleApplicationTestPage.html vous devriez voir une page blanche avec un cercle bleu au milieu.

Personnalisation du control

Ce cercle est bien beau :) mais un peu trop rigide à mon goût, supposons qu’on veuille rendre notre contrôle un peu plus personnalisable en donnant la possibilité de changer sa couleur de remplissage. Pour cela nous allons créer une Depenedency Property de type Brush au sein de notre classe MyCircle que nous allons appeler FillColor.

public Brush FillColor
{
       get { return (Brush)GetValue(FillColorProperty); }
       set { SetValue(FillColorProperty, value); }
}

// Using a DependencyProperty as the backing store for FillColor.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FillColorProperty =
     DependencyProperty.Register("FillColor", typeof(Brush), typeof(MyCircle), null);

Cette propriété pourra désormais être définie lors de l’ajout de ce contrôle de la manière suivante :

<circle:MyCircle FillColor="Chocolate"></circle:MyCircle>

Mais pour l’instant même si notre propriété FillColor est remplie, elle ne peut agir d’aucune manière sur la couleur de fond de notre cercle. Pour cela nous allons lier la propriété Fill de l’Ellipse à notre propriété FillColor de telle manière que chaque fois que FillColor change la propriété Fill de l’Ellipse est modifiée automatiquement, voici la balise Ellipse modifiée dans le fichier generic.xaml:

<Ellipse x:Name="ACircle" Stroke="Blue" Width="100" Height="100"  Fill="{TemplateBinding FillColor}"></Ellipse>

Voilà tout,  maintenant définissez la propriété FillColor à “Chocolate” (ou à une autre couleur), recompilez la solution et lancez la page MyCircleApplicationTestPage.html vous devriez voir ceci :

image

Wednesday, December 16, 2009 6:09:00 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Au coeur des Dependency Property / Attached Property#

Nous avons parlé dans un précédent post du databinding WPF/Silverlight en expliquant qu’est ce que le databinding, les Dependency Property, Attached Property et DataContext. Dans cet article nous allons nous intéresser plus particulièrement aux Dependency Property et aux Attached Property pour voir comment sont-ils implémentés.

Dependency Properties

Pour ce faire, nous allons commencer par créer une Dependency property, nous disposons pour cela du snippet VS, positionnez vous dans le code behind d’un fichier xaml et tapez propdp puis 2 fois sur la touche TAB.

Voici le code créé :

public int MyProperty
{
      get { return (int)GetValue(MyPropertyProperty); }
      set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register("MyProperty", typeof(int), typeof(MyType), new PropertyMetadata(MyPropertyChanged));

Dans le code créé par défaut par ce snippet, j’ai modifié deux petites partie : le type au quel on rajoute la Dependency Property (MyType) et une nouvelle instance de PropertyMetaData à la quelle on passe en paramètre le délégué qui traite le callback de l’évènement DependencyPropertyChanged (MyPropertyChanged). On s’arrête juste un petit instant sur ce point : On avait dit l’article précédent qu’une Dependency Property embarque des moyens de notification, à l’aide de cette méthode on va pouvoir interagir lorsque la propriété est modifiée. Voici la méthode MyPropertyChanged :

 static void MyPropertyChanged(DependencyObject obj,
                        DependencyPropertyChangedEventArgs args)
{

 }

Le premier argument est l’instance de la classe dont la propriété a été modifiée. Le deuxième argument est un objet de type DependencyPropertyChangedEventArgs qui nous met à disposition des données précieuses

image 

NewValue : Valeur de la propriété après modification (de type object)

OldValue : Valeur de la propriété avant modification (de type object)

Property : L’instance de  la Dependency Property

Dans la déclaration le la Dependency Property, nous distinguons 2 parties :

- Une propriété CLR (MyProperty), nous remarquons les méthodes GetValue et SetValue respectivement utilisées dans le Set et le Get de la propriété. Ces deux méthodes sont définies au niveau de Dependency Object c’est pour cela que la classe qui définit une Dependency Property doit être un Dependency Object. Elles servent à récupérer / mettre à jour la valeur effective de la Dependency Property. La valeur n’est donc pas stockée au niveau de l’objet qui définit la Dependency property.

- Une Dependency Property qui, comme l’indique si bien le commentaire généré, fait office de stockage pour la propriété CLR MyProperty. Intéressons nous maintenant à la méthode statique Register de la classe DependencyProperty. Si on fait un petit Reflector sur WindowsBase.dll –> System.Windows –> Dependency Property on trouve la méthode Register. Et quelques appels de méthodes plus loin on se retrouve dans la méthode RegisterCommon qui  nous intéresse. Au début de cette méthode nous retrouvons ces quelques lignes de code :

image

La classe FromKeyName est instanciée pour avoir la clé Key à partir du nom de la propriété et du type qui définit la Dependency Property, ensuite la clé résultat est utilisée pour voir si cette elle est contenue dans PropertyFromName et si c’est le cas on dit que la propriété existe déjà. Donc ce qu’on peut en tirer pour l’instant c’est que PropertyFromName contient toutes les clés des Dependency Properties. Si on regarde PropertyFromName on s’aperçoit que c’est une Hashtable, nous savons qu’une Hashtable est une collection de pairs clé / valeur. Donc on connait déjà la clé qui est la clé des Dependency property de type FromKeyName, il nous manque uniquement la valeur.

Poursuivons maintenant la lecture de la méthode, pour trouver plus loin le stockage de la Dependency Property à l’aide de cette ligne :

image

Où dp est la Dependency Property.

Le stockage des Dependency Properties se fait donc tout simplement dans une Hashtable où la clé est de type FromNameKey et la valeur est la Dependency Property.

Attached Properties

Regardons maintenant comment ça se passe pour une Attached Property. Le snippet VS pour créer une Attached Property est propa voici le code généré :

 

public static int GetTestProperty(DependencyObject obj)
{
      return (int)obj.GetValue(TestPropertyProperty);
}

public static void SetTestProperty(DependencyObject obj, int value)
{
      obj.SetValue(TestPropertyProperty, value);
}

// Using a DependencyProperty as the backing store for TestProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty TestPropertyProperty =
            DependencyProperty.RegisterAttached("TestProperty", typeof(int), typeof(Button), new PropertyMetadata(MyPropertyChanged));

Nous remarquons deux différences :

- Le getter / setter sont deux fonctions statiques au lieu d’une propriété dans le cas d’une Dependency Property. C’est grâce à ça qu’on peut bénéficier des Attached Properties sur tous les objets.

- On utilise la méthode RegisterAttached au lieu de Register. En creusant un peu avec Reflector WindowsBase.dll –> System.Windows –> Dependency Property on trouve la méthode RegisterAttached qui appelle une autre méthode appelée RegisterAttached qui appelle elle même la méthode RegisterCommon appelée lors du Register d’une Dependency Property. On retombe donc sur nos pieds!!!

image

Le stockage d’une Attached Property se fait de la même manière que pour une Dependency Property c’est à dire dans une Hashtable.

c# | c#3 | Silverlight | Silverlight 3 | WPF
Thursday, November 26, 2009 10:49:42 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Databinding : Dependency property, Attached property, DataContext#

Le data binding lie un composant UI qui hérite de System.Winwdows.FrameworkElement à une source de données, pour cela il y a deux conditions fondamentales :

- Il faut que le composant cible du data binding soit un FrameworkElement

- Il faut que la propriété cible soit une DependencyProperty

Nous avons vu dans ce post comment on arrive à lier une grille à une source de données, pour rappel nous avons utilisé cette ligne de code :

<Data:DataGrid x:Name="TasksGrid" ItemsSource="{Binding Tasks}">
</Data:DataGrid>

A voir cette ligne, nous pouvons penser que c’est magique, pour comprendre comment ça marche derrière, nous allons écrire la même chose par code. Ça donne ça :

TasksViewModel tasksvm = new TasksViewModel();

Binding bind = new Binding("Tasks");
TasksGrid.SetBinding(DataGrid.ItemsSourceProperty, bind); 

//Partie existante
this.DataContext = tasksvm;
tasksvm.FillTasks();

Si nous traduisons les 2 lignes qui nous intéressent, nous commençons par instancier un objet de type Binding en lui donnant en paramètre le nom de la propriété du type de l’objet au quel la grille va être liée. Ensuite nous disons à la grille qu’elle doit utiliser l’objet bind de type Binding pour la liaison de sa propriété ItemSource. Donc en fait ce que nous pouvons en tirer c’est que la liaison entre la grille et la source de données ne se fait pas automatiquement mais qu’il y a un objet de type Binding qui régit cette liaison. Cet objet va notifier le moteur de binding à chaque fois qu’il y a un changement dans la source de données, ce moteur va mettre à jour la Dependency property correspondante (ItemSource dans notre exemple) et va ordonner un rafraîchissement du composant graphique.

Depenedency property

La dependency property est une propriété .NET étendue qui embarque des moyens de notification et de liaison avec une source de données et dont la valeur n’est connue qu’à l’exécution (Runtime). Elle peut enrichir n’importe quelle classe du moment que cette dernière est un Dependency object mais elle est majoritairement utilisée dans les composants graphiques.

DataContext

Le DataContext est une propriété que l’on retrouve dans tous les contrôles bindable à une source de données. Elle est héritée du contrôle parent et peut être redéfinie au niveau de l’enfant. Elle est utilisée essentiellement pour le databinding, elle représente la source de données et contient en général une instance d’une entité. Si on reprend notre exemple ci-dessus :

<Data:DataGrid x:Name="TasksGrid" ItemsSource="{Binding Tasks}">

Avec {Binding Tasks} on dit que la propriété ItemsSource du datagrid est une propriété liée à la propriété Tasks de l’objet de liaison, mais où est ce qu’on définit l’objet de liaison? En effet, l’objet de liaison n’est rien d’autre que le DataContext, donc il suffit de peupler le DataContext avec l’objet contenant la propriété Tasks et le moteur de binding ira chercher les données de cet emplacement lorsqu’il le faudra.

Attached Property

Une Attached Property c’est le même principe qu’une Dependency Property à une exception près : Tous les Dependency Object peuvent bénéficier d’une Attached Property. Vous allez me dire, mais à quoi ça sert ??? En effet, dans certains cas, une propriété d’un élément graphique ne prend sens que dans un certain contexte, donc pour éviter que tous les éléments graphiques embarquent toutes les propriétés possibles et imaginables dans tous les différents contextes qui existent, les attached property ont été créés . C’est encore flou :) Prenons un exemple : considérons un DockPanel qui contient des éléments graphiques.

<DockPanel>
         <TextBox DockPanel.Dock="Left"/>
</DockPanel>

Le contrôle TextBox ne possède pas la propriété Dock mais DockPanel possède une AttachedProperty appelée Dock et comme on a dit que tous les éléments peuvent bénéficier d’une Attached Property, tous les éléments graphiques peuvent utiliser cette propriété, c’est ce qui fait que TextBox peut renseigner DockPanel.Dock à Left. La propriété Dock ne prend sens pour un TextBox que lorsque celui-ci est contenu dans un DockPanel et son utilisation hors de ce contexte est obselète. Une question qui nous vient à l’esprit tout de suite : mais si la propriété est appelée dans un mauvais contexte ? Si le contexte d’appel est inapproprié (une textbox qui n’est pas contenue dans un DockPanel et qui renseigne DockPanel.Dock) la propriété est ignorée.

Comme les Dependency Property, les Attached Property bénéficient de l’héritage hierarchique, elle peut donc être définie au sein d’un élément parent pour être répercutée sur tous ses éléments enfants comme elle peut être redéfinie au niveau d’un élément enfant.

Conclusion

Les concepts énoncés dans cet article sont fondamentaux pour toute personne voulant travailler avec des technologies comme Silvertlight ou WPF. Mais comment ça marche derrière ? Comment sont stockés les valeurs renseignées pour les Attached Property ? C’est ce que nous allons voir dans un prochain post.

c# | c#3 | Silverlight | Silverlight 3 | WPF
Wednesday, November 18, 2009 11:44:40 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Silverlight 3 : analyse d’un projet « Silverlight navigation application »#

Pour ceux qui se demandent comment marche Silverlight et par quel bout prendre la chose, cet article est fait pour vous. Dans cet article on va analyser la coquille vide créée par Visual Studio pour bien comprendre les mécanismes mis en évidence dans le projet exemple.

Nous allons commencer par créer un projet de type « Silverlight navigation application » (Si vous avez besoin d’aide, reportez vous à ce post)

Concentrons nous maintenant sur la solution générée, qui doit avoir cette allure :

image

Nous avons donc 2 projets :

- NavigationApp.web : application web qui va contenir notre application Silverlight.

- NavigationApp : notre application silverlight

NavigationApp.web

Le projet est une application web, nous remarquons les deux pages de tests suivantes : NavigationAppTestPage.aspx NavigationAppTestPage.html. Ces deux pages mettent en œuvre l’hébergement de l’application Silverlight NavigationApp.xap en se basant sur le fichier Silverlight.js. Si on ouvre la page html (ou aspx) on va trouver essentiellement la balise Object qui va nous permettre d’inclure notre application Silverlight dans la page.

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
          <param name="source" value="ClientBin/NavigationApp.xap"/>
          <param name="onError" value="onSilverlightError" />
          <param name="background" value="white" />
          <param name="minRuntimeVersion" value="3.0.40818.0" />
          <param name="autoUpgrade" value="true" />
          <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40818.0" style="text-decoration:none">
               <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
          </a>
        </object>

Dans la balise object, nous pouvons définir plusieurs paramètres dont :

- Source : qui est le binaire à télécharger.

- onError : la fonction javascript à executer lorsqu’une exception non catchée est levée au sein de l’application Silverlight.

- backgroung : la couleur de fond de l’application silverlight.

- minRuntimeVersion : La version minimale du plugin silverlight pouvant exécuter le binaire.

- autoUpgrade : Définit une valeur qui indique si une version du plug-in Silverlight antérieure au minRuntimeVersion spécifié tente de se mettre à jour automatiquement.

La liste complète des paramètres que nous pouvons définir pour une balise object est ici, ce qu’il faut retenir c’est que dès que nous voulons changer quelque chose par rapport à l’intégration de l’application ou du plugin silverlight au sein de la page c’est de ce côté ci qu’il faut regarder :)

L’iframe « _sl_historyFrame » est utilisée pour gérer l’histoique de navigation au sein de l’application Silverlight. Elle est partagée par toutes les applications Silverlight dans une même page.

Vous avez certainement remarqué que le fichier Silverlight.js est inclus dans ces pages de test. Ce fichier permet principalement de faire 2 types de fonctionnalités :

- Inclure programmatiquement une application Silverlight.

- Améliorer la procédure d'installation ou de mise à niveau standard de Silverlight.

Vous trouverez ici la liste des fonctions que propose ce fichier js.

Je pense qu’on a fait le tour de l’application web, tournons nous maintenant vers l’application Silverlight.

NavigationApp

C’est le projet qui contient notre application silverlight. Le point d’entrée de cette application est le fichier App.xaml, donc commençons déjà par analyser ce fichier. Son contenu devrait ressembler à ça :

<Application   
  x:Class="NavigationApp.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Assets/Styles.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>

</Application>

On voit tout de suite que c’est une application avec la balise <Application>. Dans ce fichier on va définir toutes les ressources au niveau de l’application, ici à titre d’exemple, la ressource est un fichier xaml contenant tous les styles de l’application.

Dans le code behind de l’App.xaml on s’abonne aux évènements « Startup » et « UnhandledException » en définissant le comportement de l’application lorsque ces deux évènements sont déclanchés.

Lors du démarrage de l’application, l’évènement « Startup » est déclanché, si on regarde le comportement de l’application au Startup nous allons trouver la ligne de code suivante :

this.RootVisual = new MainPage();

A l’aide de cette ligne nous allons instancier notre conteneur d’écrans (pages) qui est ici MainPage.

Allons du coup du côté de MainPage.xaml : La première chose que nous allons remarquer c’est que MainPage est un UserControl, on le voit à l’aide de la balise <UserControl> au tout début du fichier xaml. Mais au fait, comment est chargé le xaml ? parce que ce qu’on a vu c’est qu’on fait un new MainPage(). Si nous regardons le constructeur dans le code behind de Mainpage.xaml nous allons nous apercevoir qu’il y a une méthode qui s’appelle InitializeComponents

En effet, dans le « Solution Explorer », positionnez vous au niveau du projet et cliquer sur l’icône « Show all files » comme suit :

image

Vous allez voir apparaître les répertoires cachés bin et obj. Dans le répertoire obj -> Debug ouvrez le fichier qui s’appelle MainPage.g.cs. Vous allez y trouver la méthode InitializeComponents que nous cherchions. Dans cette méthode nous allons charger le xaml et tous ces contrôles. Vous trouverez un fichier .g.cs pour chaque xaml que vous créez dans votre projet.

Retournons à notre xaml MainPage maintenant, nous avons dit qu’il contient une balise <UserControl> qui disait que MainPage était un UserControl, cette balise contient quelques attributs lui pêrmettant de charger les espaces de noms de librairies comme Navigation ou Control … Ensuite on trouve 2 grandes parties :

1 - Navigation frame

 

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" 
                              Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">
                <navigation:Frame.UriMapper>
                  <uriMapper:UriMapper>
                    <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>
                    <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
                  </uriMapper:UriMapper>
                </navigation:Frame.UriMapper>
            </navigation:Frame>

Navigation frame va acceuillir une navigation page qui sera définie par la logique d’uri mapping. En fait notre UserControl MainPage joue le rôle de conteneur comme une MasterPage en ASP.NET : nous allons mettre dedans l’allure générale que doivent avoir nos pages et nous allons définir les zones (Navigation frame) dans les quelles nous allons acceuillir des navigation pages.

L’uri mapping, permet de faire le mapping entre l’url demandée et la page délivrée au client. Regardons tout d’abord la balise <navigation:Frame> qui est caractérisée par les attributs suivants :

- x:Name : le nom du contrôle

- Style : son style (Styles.xaml)

- Source : l’Uri par défaut

- Navigated : définit ce que doit faire l’application après qu’on ai navigué vers une page

- NavigationFailed : définit ce que doit faire l’application lorsque la navigation vers une page échoue

Ici nous définissons quelques règles de mapping d’uri en disant que si la page demandée est vide (Uri= «») donc l’url demandée ressemble à celle-ci : http://mondomaine/NavigationAppTestPage.aspx la navigation frame va alors contenir la page par défaut qui est /Views/Home.xaml. Et si la page demandée est /{PageName} la navigation frame va alors contenir la page /Views/{PageName}.xaml. Essayons maintenant d’analyser notre situation de départ en démarrant le projet, nous allons lancer la page NavigationAppTestPage.aspx et le navigation frame va demander l’Uri « /Home » définie dans l’attribut « Source » de la balise « navigation :Frame ». l’Uri /Home va donc être mappée à /Views/Home.xaml grâce à cette règle :

<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>

et la navigation frame va donc contenir la page Home.xaml.

2- Banière

        <Grid x:Name="NavigationGrid" Style="{StaticResource NavigationGridStyle}">

            <Border x:Name="BrandingBorder" Style="{StaticResource BrandingBorderStyle}">
                <StackPanel x:Name="BrandingStackPanel" Style="{StaticResource BrandingStackPanelStyle}">

                    <ContentControl Style="{StaticResource LogoIcon}"/>
                    <TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}" 
                               Text="Application Name"/>

                </StackPanel>
            </Border>

            <Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}">
                <StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}">
                    <HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}"
                                   NavigateUri="/Home"   TargetName="ContentFrame" Content="home"/>
                                     
                    <Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>

                    <HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}" 
                                     NavigateUri="/About" TargetName="ContentFrame" Content="about"/>

                </StackPanel>
            </Border>
        </Grid>

Ce code permet de dessiner la banière qu’il y a en haut avec le logo, le nom de l’application et les boutons, nous n’allons pas nous attarder sur cette partie parce que c’est uniquement l’utilisation de composants graphique.

Le répertoire /Views

Concentrons nous tout d’abord sur Home.xaml et About.xaml. Ces deux fichiers sont de type navigation page. Les navigation pages est un apport de la version 3 de Silverlight. En fait, Silverlight 3 embarque un framework de navigation et du coup nous pouvons désormais naviguer entre les pages xaml d’une application Silverlight en utilisant le navigateur.

Il ne nous reste plus qu’à jeter un coup d’œil sur la page ErrorWindow.xaml qui est dans le répertoire Views. Cette page est de type childWindow, dans la terminologie web c’est une popup.

Résumé

Voila voila, l’analyse du projet exemple est finie, nous avons vu :

- Que App.xaml est le point d’entrée de l’application Silverlight.

- Que dans App.xaml nous pouvons définir les ressources de l’application.

- Dans App.xaml.cs, nous définissons notre UserControl conteneur en faisant this.RootVisual= new MainPage() dans le code behind de App.xaml.

- Dans MainPage.xaml nous définissons l’ossature de l’application (un peu comme une Master Page) et les zones (Navigation Frame) qui peuvent acceuillir du contenu(Navigation Pages).

- Que dans une Navigation frame nous pouvons définir l’uri mapping.

- Les pages Home et About sont de type Navigation Page et sont hébergés par une navigation frame.

- Silverlight 3 embarque un framework de navigation permettant de naviguer entre les pages d’une application silverlight.

- Une ChildWindow est l’équivalent d’une popup dans le monde web.

Il reste néanmoins énormément de choses à voir comme les composants graphiques, comment accéder aux données serveur (puisqu’on est côté client), Les dependency proporty et attached property, DataBinding, … nous allons essayer de couvrir tous ces points dans de prochains posts.

Tuesday, October 20, 2009 8:19:00 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Instancier une classe générique avec un type dynamique et appeler une méthode appartenant à cette classe.#

J’ai rencontré ce problème en développant une couche d’accès aux données générique avec le compact framework. Voici le problème : J’ai développé une classe générique CRUD<T> qui permet de faire du CRUD simple. Pour ça j’ai utilisé les attributs pour décorer mes propriétés d’entités et dire que telle classe correspond à telle table et que telle propriété correspond à tel champ dans cette table. Ensuite et à l’aide de System.Reflection on regarde les attributs de la classe et des propriétés, on génère le sql correspondant à l’entité passée en paramètre et on exécute de la base.

Bon tout ça marche bien, maintenant supposons que l’on ait la classe suivante :

    public class Personne
    {

        int id;

        List&lt;Voiture&gt; Voitures;

    }

Il faudrait donner la possibilité d’auto charger les voitures d’une personne au même moment qu’on charge une personne. Donc on rajoute un attribut sur la propriété de type List<Voiture>dans la classe Personne pour dire que c’est une entité liée et en parcourant les attributs de ma classe pour générer le sql, quand je rencontre ce type d’attribut, je voudrais rappeler ma classe CRUD<T> avec T = Voiture pour obtenir la liste de voitures correspondantes à la personne. Or on ne sait pas par avance que c’est des Voitures ce n’est qu’au Runtime que je saurais que c’est des voitures donc le problème est le suivant : invoquer ma classe CRUD<T> avec un T dynamique.

Pour ce faire il y a 2 solutions ou plutôt 2 types de solutions :

- Contourner le problème en disant par exemple que de toutes les façons que c’est nul de vouloir auto charger les liens et de laisser ça à la demande du développeur.

- Utiliser ce qui existe dans le compact framework pour réussir

Je me suis plutôt focalisé sur le 2ème type de solution même si je ne suis pas en désaccord total avec le 1er type.

On dispose donc d’une variable « typeVoiture » de type « Type » qui contient le type « Voiture »

Pour créer le type CRUD<Voiture> on a à notre disposition dans Type la méthode MakeGenericType, on peut donc procéder de la manière suivante :

Type crud = typeof(CRUD&lt;&gt;);
Type[] typeArgs = { typeVoiture }; 
Type repositoryType = crud.MakeGenericType(typeArgs);

Et pour en créer une instance il suffit d’utiliser Activator.CreateInstance de la manière suivante :

object repository = Activator.CreateInstance(repositoryType);

On remarque que notre instance est de type « object », on ne peut donc pas utiliser directement ses méthodes et on ne peut pas le caster non plus puisqu’on ne peut pas écrire CRUD<typeVoiture.GetType()>.

Le seul moyen (que je connaisse en tout cas) est le suivant : On construit un objet de type « MethodInfo » à partir de l’instance de type générique qu’on a créé précédemment. Ensuite, on construit un tableau d’object qui correspond aux paramètres de la méthode à appeler et enfin on l’appelle en utilisant la méthode Invoke.

MethodInfo genericMethod = repositoryType.GetMethod("GetByForeignKey"); paramList = new object[2]; paramList[0] = param1; paramList[1] = param2;

genericMethod.Invoke(repository, paramList);

SI vous connaissez un autre moyen de faire, ou si vous voulez juste réagir, nhésitez pas, commentez ce post ;)

Thursday, October 01, 2009 9:43:25 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

All content © 2012, Zied Nemili