Magazine Internet

Bind de contrôles ASP à partir d'un Objet

Publié le 10 septembre 2008 par Jeremy.jeanson

"Bind de contrôles ASP à partir d'un Objet": voila un tire qui ressemble à ce que tout le monde fait tous les jours... On prend un ObjectDataSource et c'est parti.

En fait le code présenté ici n'a rien à voire. En général nos formulaire sont constitués des contrôles divers, bien souvent des Textboxs et des Labels, et chacun de ces contrôles correspond avec une information provenant d'une base de données (BDD). Dans le cas où l'on souhaite utiliser une architecture n tiers (BO, DAL, BLL) on utilise une classe qui n'a rien à voir avec notre BDD... Et là, il faut pour chaque propriété de cette classe alimenter le contrôle que l'on a mis sur notre formulaire (pas drôle).

En général passé le dixième ou vingtième contrôle (voir dixième ou vingtième café), un gros soupir de lassitude se fait entendre. Dans certain cas on assiste même à une légère crampe du poignet... (pas faciles les métiers du développement)

Viens ensuite le moment de la démarche inverse, où il faut relire les informations de chaque contrôle et les passer à la classe qui serra passée à notre BLL...

Ces deux démarches n'étant pas ce qu'il y de plus réjouissant, j'ai développé deux méthodes qui font le travail à ma place:

  • La première méthode d'extension affiche les propriétés d'une classe.
  • La seconde alimente la classe à partir de la saisie de l'utilisateur.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Tools
{
    public static class PageExtension
    {
        /// <summary>
        /// Lire les propriété d'un objet et les associer aux controls d'un formaulaire 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sender"></param>
        /// <param name="sourceDeDonnees"></param>
        public static void Bind<T>(this Control sender, T sourceDeDonnees)
        {
            var req = (
                // Liste des propriétés
                from data in typeof(T).GetProperties()

                // Liste des controls
                from control in sender.ControlsAvecRecursivite()

                // Mémorisation de la propriété du control que l'on pourait binder
                let propertie = control.GetType().GetProperties()
                    .Where(c => c.Name == "Text" || c.Name == "Value")
                    .OrderByDescending(c => c.Name) // Pour récupérer en priorité la propriété "Value"
                    .FirstOrDefault()

                // Liaison de la propriété avec le control dont l'id commande par "Bind" et fini par le nom de la propriété
                // Et dont une propriété est bindable
                where !String.IsNullOrEmpty(control.ID)
                    & control.ID.EndsWith(String.Concat("Bind", data.Name))
                    & propertie != null

                // Retour
                select new
                {
                    data,
                    control,
                    propertie
                })

                .ToArray();

            // Test si on a des éléments communs
            if (req != null
                & req.Length > 0)
            {
                // Parcour de la liste des éléments trouvé
                for (Int32 i = 0; i < req.Length; i++)
                {
                    // Set de la propriété du control en fonction du type de culi-ci

                    // Set la propriété voulu
                    req[i].propertie.SetValue(
                        // Du control
                        req[i].control,
                        // En convertissant le type
                        Convert.ChangeType(
                        // De la source de données
                            req[i].data.GetValue(sourceDeDonnees, null),
                        // Vers le type voulu par le control
                            req[i].propertie.PropertyType),
                        null);
                }
            }

        }

        /// <summary>
        /// Lire les controls d'un formaulaire et retourner dans un objet leurs valeurs
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sender"></param>
        /// <returns></returns>
        public static T Bind<T>(this Control sender) where T : new()
        {
            T retour = new T();

            var req = (
                // Liste des propriétés
                from data in typeof(T).GetProperties()

                // Liste des controls
                from control in sender.ControlsAvecRecursivite()

                // Mémorisation de la propriété du control que l'on pourait binder
                let propertie = control.GetType().GetProperties()
                    .Where(c => c.Name == "Text" || c.Name == "Value")
                    .OrderByDescending(c => c.Name) // Pour récupérer en priorité la propriété "Value"
                    .FirstOrDefault()

                // Liaison de la propriété avec le control dont l'id commande par "Bind" et fini par le nom de la propriété
                // Et dont une propriété est bindable
                where !String.IsNullOrEmpty(control.ID)
                    & control.ID.EndsWith(String.Concat("Bind", data.Name))
                    & propertie != null

                // Retour
                select new
                {
                    data,
                    control,
                    propertie
                })

                .ToArray();

            // Test si on a des éléments communs
            if (req != null
                & req.Length > 0)
            {
                // Parcour de la liste des éléments trouvé
                for (Int32 i = 0; i < req.Length; i++)
                {
                    // Set de la propriété du control en fonction du type de culi-ci
                    String valueS = req[i].propertie.GetValue(req[i].control, null).ToString();
                    // Convertion de la valeur si elle n'est pas null
                    var value = String.IsNullOrEmpty(valueS)
                        ? null
                        : Convert.ChangeType(valueS,
                        // Avant de choisir le type, on détermine si il est Nullable
                            req[i].data.PropertyType.IsNullable()
                            ? Nullable.GetUnderlyingType(req[i].data.PropertyType)
                            : req[i].data.PropertyType);

                    // Set la propriété voulu
                    req[i].data.SetValue(
                        // Du control
                        retour,
                        value,
                        null);

                }
            }

            return retour;
        }

        /// <summary>
        /// Retouner une liste de tous les controls contenus dans un control sans oublier les controls enfant de ceux-ci
        /// </summary>
        /// <param name="sender"></param>
        /// <returns></returns>
        public static IEnumerable<Control> ControlsAvecRecursivite(this Control sender)
        {
            // Lister les controls enfant
            foreach (Control c in sender.Controls)
            {
                // Retourner le control enfant
                yield return c;
                // Pour chague control retourner ses controls enfant via l'enumération [Recursivite]
                foreach (Control child in c.ControlsAvecRecursivite())
                {
                    yield return child;
                }
            }
        }
    }
}

Avec T provenant de ma BLL et Control correspond  au contrôle contenant les contrôles de mon formulaire. En fin de code on retrouve une méthode maison qui permet de lister tout les contrôles, même ceux qui sont au sein d'autres contrôles.

Ce code est donc des plus générique et peut être adapté à toutes les situations.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Jeremy.jeanson 1573 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte

Dossier Paperblog