"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.