Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Thomas Lebrun

Tout sur WPF, LINQ, C# et .NET en général !

[WPF] Créer vos classes d’objets métier en quelques clics de souris !

Ce qui me connaissent bien savent que j’ai toujours une grande quantité de projets en cours et que, faute de temps, ceux-ci n’avancent pas vite. L’un d’eux devrait bientôt voir le jour mais pour le moment, en voici un autre un peu moins…. important (si je puis dire Smile).

Pour les besoins d’un projet WPF récent, j’ai eu besoin de créer plusieurs classes me servant d’objet métier et cela a été l’occasion pour moi de mettre au point un outil que j’avais depuis longtemps en tête.

Tout le monde est capable de créer une classe et ses quelques propriétés: les snippets de Visual Studio permettent de faire gagner un temps précieux. Seulement avec WPF, il est conseillé de faire implémenter l’interface INotifyPropertyChanged à ces objets, pour que ceux-ci soient en mesure d’informer l’interface utilisateur qu’ils ont été modifié.

Or, dans ce cas, les snippets de Visual Studio 2008 ne sont pas d’une très grande aide car ils génèrent des propriétés automatiques (i.e des propriétés dont le membre privé est généré à la compilation) et sur lesquelles il n’est pas possible de rajouter de code (à moins de le faire à posteriori):

public class Demo

{

    public int ID { get; set; }

    public string Name { get; set; }

}

Afin de me permettre de gagner un maximum de temps dans la génération de mes objets métier, j’ai créé un petit addin à Visual Studio (que je devrais bientôt mettre sur Codeplex) et qui, grâce à un peu de CodeDom, me permet de générer toutes mes classes, entièrement “compatible” avec WPF, ainsi qu’une collection d’objet métier (collection qui hérite de ObservableCollection):

image

image

image

Le code généré est propre et possède même des commentaires Stick out tongue:

//------------------------------------------------------------------------------

// <auto-generated>

//    This code was generated by a tool.

//    Runtime Version:2.0.50727.3053

//

//    Changes to this file may cause incorrect behavior and will be lost if

//    the code is regenerated.

// </auto-generated>

//------------------------------------------------------------------------------

 

namespace WizardBase

{

    using System.ComponentModel;

    using System.Collections.ObjectModel;

 

 

    /// Implementation of the class named Product

    public class Product : System.ComponentModel.INotifyPropertyChanged

    {

 

        #region Property ID

        /// <summary>

        /// Private field used for the property ID

        /// </summary>

        private int m_ID;

 

        /// <summary>

        /// Description for the property ID

        /// </summary>

        protected virtual int ID

        {

            get

            {

                return m_ID;

            }

            set

            {

                m_ID = value;

                this.RaisePropertyChanged("ID");

            }

        }

        #endregion

 

        #region Property Name

        /// <summary>

        /// Private field used for the property Name

        /// </summary>

        private string m_Name;

 

        /// <summary>

        /// Description for the property Name

        /// </summary>

        protected virtual string Name

        {

            get

            {

                return m_Name;

            }

            set

            {

                m_Name = value;

                this.RaisePropertyChanged("Name");

            }

        }

        #endregion

 

        #region INotifyPropertyChanged Members

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        #endregion

 

        #region Private Methods

        /// <summary>

        /// Method used to raise the event PropertyChanged

        /// </summary>

        private void RaisePropertyChanged(string propName)

        {

            if ((PropertyChanged != null))

            {

                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propName));

            }

        }

        #endregion

    }

 

    /// <summary>

    /// Implementation of the collection named ProductCollection

    /// </summary>

    public class ProductCollection : ObservableCollection<Product>

    {

    }

}

Il me faut encore finaliser 2-3 petites choses avant de mettre ceci en release mais je compte bien rajouter un maximum de fonctionnalités à cet addin donc si vous avez des idées, n’hésitez pas ! Wink

 

A+

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: vendredi 19 septembre 2008 10:21 par Thomas LEBRUN
Classé sous : , ,

Commentaires

wizad a dit :

Ca m'intéresse ça... (perso je m'était fait un snippet pour obtenir des propriété avec le notify)

Juste 2 questions :

- la classe métier généré utilise quel namespace?

- Au niveau des type dispo dans l'ui, tu gère les type définis dans le projet aussi?

Bon courage pour la suite ;)

# septembre 19, 2008 10:56

Julien237 a dit :

Génial !

Je trouvais que ça manquait aussi...

Juste une petite remarque :

Ne serait-il pas judicieux de vérifier dans le bloc set que la valeur est effectivement différente, i.e. mettre par exemple :

set

{

   if (m_ID != value)

   {

       m_ID = value;

       this.RaisePropertyChanged("ID");

   }

}

# septembre 19, 2008 11:49

simon ferquel a dit :

Faudrait aussi générer des classes partielles, et des méthodes partielles dans les setters par exemple.

Si tu as besoin d'aide pour la liste des types accessibles, vas voir dans le code de VLinq, tu as des bouts de code qui utilise les services de "Type Discovery" de Visual Studio :)

# septembre 19, 2008 13:42

Thomas LEBRUN a dit :

@wizad: La classe générée utilise le namespace du projet sur lequel tu fais l'insertion de la classe. Pour les types gérés, pour le moment, je ne gère que les types de bases mais avec ce que vient de dire Simon, ca va p-e changer :)

@Julien: Effectivement, on pourrait rajouter ce test mais bon d'un coté, il faudrait voir ce qui coute le plus cher: ajouter un test/une vérif ou toujours faire l'affectation

@Simon: Les classes et les méthodes partielles, c'est prévu :) Et merci pour le tips, je vais voir ca ;)

# septembre 19, 2008 14:24

Jem a dit :

"Effectivement, on pourrait rajouter ce test mais bon d'un coté, il faudrait voir ce qui coute le plus cher: ajouter un test/une vérif ou toujours faire l'affectation"

Ce n'est pas tant une question de coût du test que de déclenchement intempestif d'évènements qui pourraient aboutir à une boucle infinie.

# septembre 19, 2008 16:29

Thomas LEBRUN a dit :

J'ai beau chercher mais je ne vois pas comment tu voudrais aboutir à une boucle infinie simplement en lançant un évènement (tout du moins dans le cas qui nous intéresse)....

# septembre 19, 2008 17:15

Jem a dit :

Excuse j'ai dit ça sans trop réfléchir, je n'ai pas de cas précis en tête.

Je pensais à un code s'abonnant à PropertyChanged pour calculer certaines propriétés en fonction d'autres et ainsi se verrait appelé récursivement un peu trop facilement.

Par contre si on veut utiliser PropertyChanged pour noter que l'objet à été modifié et donc devra être enregistré, il me semble intéressant que l'évènement ne soit pas remonté systématiquement.

# septembre 19, 2008 17:32

Julien237 a dit :

Il y a plusieurs raisons selon moi :

1) D'abord parce que ça semble logique, pourquoi déclencher un évènement Changed lorsque la valeur n'a pas changé ?

2) Le problème de boucle infinie survient bien dans certain cas, par exemple on veut, dans une TextBox, modifier le texte saisi pour qu'il réponde à une condition. Par exemple on pourrait vouloir forcer la frappe majuscule, et dans l'évènement Changed mettre

textBox.Text = textBox.Text.ToUpper();

C'est un exemple un peu naif, mais d'autres plus recherchés peuvent apparaitre (je n'ai pas d'exemple en tête, mais je me souviens d'avoir été forcé de mettre cette vérification dans des cas plus complexes...)

3) Si le code dans PropertyChanged est conséquent (rafraichissement visuel, mise à jour de variables dérivées par calcul, accès disque etc...), il peut être impératif de ne pas déclencher des PropertyChanged intempestifs...

Bien sûr l'utilisateur peut dans le PropertyChanged vérifier manuellement que la valeur a bien changé, mais bon...

Quelques tests donnent 2.24E-5 ms sans vérification, et 2.32E-5 ms avec vérification, en prennant une valeur différente à chaque fois pour la propriété.

J'obtiens des temps à peu près égaux pour 2% de d'appels avec la même valeur. Le test est fait une méthode RaisePropertyChanged vide.

Il n'est pas inimaginable que le temps d'appel d'un vrai RaisePropertyChanged et des abonnés soit de l'ordre de 1000 fois le petit test booléen rajouté, auquel cas, le pourcentage précité descendrais encore drastiquement...

Donc pour moi la question de savoir si c'est rentable en temps est résolue...

# septembre 19, 2008 19:03

Julien237 a dit :

Je ne voudrais pas que mon dernier long et un peu vindicatif message soit pris pour de la critique gratuite d'un petit détail, l'idée est chouette et c'est une aide non-négligeable apportée à la communauté, merci !

# septembre 19, 2008 19:05

Thomas LEBRUN a dit :

@Jem: Oui, ce à quoi tu pensais peut avoir du sens mais pas dans le cas présent :)

@Julien:

1) Effectivement, c'est une bonne raison :)

2) Dans ton exemple, elle est où la liaison avec un objet métier (et donc l'utilisation de l'évent) ? Nul part donc pas de boucle infinie

3) Pourquoi ne pas déclencher des PropertyChanged intempestifs ? Au pire, ton IHM en patira un coup mais c'est tout. De plus, pour que cet évènement se déclenche, il faut que la propriété soit modifiée donc à moins de faire une boucle dans laquelle tu mets à jour la propriété, cela a peut de chance de se produire :)

# septembre 19, 2008 19:17

Julien237 a dit :

2) Il est vrai que dans le cas présent, il s'agit d'un contrôle et non d'un objet métier, mais un exemple plus complet serait

public class TextBox

{

.....

   public string Text

   {

      get { ... }

      set

      {

          ...

          RaiseChangedProperty();

      }

    }

}

Dans une classe de l'interface graphique

void TextBox_Changed(...)

{

   textBox.Text = textBox.Text.ToUpper();

}

L'évènement sera déclenché à l'infini.

Je suis sûr qu'on peut trouver des exemples équivalents avec des objets métiers.

# septembre 19, 2008 20:11

Yolk_gf a dit :

Super boulot Thomas.

J'ai une proposition de fonctionnalité à ajouter:

Ce serait la possibilité de créer l'objet métier serializable. Utilisant beaucoup WCF, cette fonctionnalité serait bien pratique pour moi ( et peut-être pour d'autres...)

# septembre 22, 2008 09:51

Thomas LEBRUN a dit :

Salut Yolk_gf,

Excellente proposition: je le met sur ma TODO :)

Merci !

# septembre 22, 2008 10:04

EliseD a dit :

sympa l'outil

aviez-vous vu celui la ?

http://drwpf.com/blog/Home/tabid/36/EntryId/22/Default.aspx

# septembre 24, 2008 12:35

Thomas LEBRUN a dit :

Oui, j'était au courant de ces snippets: cela fait même un moment que je les ai téléchargé ;)

# septembre 24, 2008 13:04

kbuntu a dit :

L'idée est intéressante. Pour autant je connais une autre alternative pour la génération des classes métiers très bien réalisée. Il s'agit de générer les classes à partir d'un schéma XSD. L'avantage est de pouvoir inclure le schéma dans le projet et grace à un addin il est possible de re-générer en changant les options de génération. J'utilise cet outil depuis maintenant plus d'un an et il apporte un gain de productivité non négligeable.

http://www.codeplex.com/Xsd2Code

# septembre 28, 2008 09:44
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- [Refactoring] ReSharper pour Visual Studio 2010 (Preview) par Thomas Jaskula le il y a 14 heures et 21 minutes

- [Refactoring] Analyser vos exceptions avec ReSharper Exceptional par Thomas Jaskula le il y a 15 heures et 35 minutes

- SharePoint 2007 : patterns & practices SharePoint Guidance par Philippe Sentenac [MVP SharePoint] le 07-03-2009, 09:56

- [Visual Studio 2010] Les tests cases c’est bien, mais je vais devoir tout réécrire ? par Etienne Margraff le 07-03-2009, 09:00

- MVP[Gribouillon].AddYear par The Grib's Lair [Sébastien PICAMELOT - MVP SharePoint] le 07-03-2009, 08:45

- Clinique INSIA - Projet de fin d’Etudes (Silverlight 3 MVVM et OutOfBrowser, WCF, TFS) - Part 1 par David REI le 07-02-2009, 23:38

- C’est la crise ? Bah pourquoi cramer du budget pub alors ? par Nix's Blog le 07-02-2009, 15:31

- Soyons MVP ! par TheSaib .NET blog le 07-02-2009, 12:15

- SharePoint : Gestion des Erreurs 6398, 7076 et 6482 par Blog Technique de Romelard Fabrice le 07-02-2009, 11:53

- EF avec WPF par Matthieu MEZIL le 07-02-2009, 10:18