Version imprimable       Envoyer     
Cliquez pour évaluer et commenter
MSDN
MSDN Library
Articles Techniques
Développement .NET
Articles et Vue d'ensemble
Application ASP.NET
ASP.NET
ASP.NET 2.0
 Création de listes déroulantes imbr...
Création de listes déroulantes imbriquées dans un formulaire ASP.NET 2.0
Paru le 01 novembre 2006
Par Christine DUBOIS et Bernard FEDOTOFF, Microsoft Regional Directors, consultants, AGILCOM

Mots clés
ASP.NET 2.0, DropDownList, FormView, Liaison de données, databinding, cascade, imbriqué.

La problématique
L’idée est d’utiliser dans les modèles des contrôles d’affichage de données ASP.NET 2.0 du type FormView, GridView ou DetailsView, des listes déroulantes (DropDownList) imbriquées les unes par rapport aux autres. Sur le principe, c’est très simple, sauf qu’il n’est pas possible d’utiliser le databinding déclaratif de façon usuelle du fait de l’imbrication des listes.

Sur cette page

Exemple fonctionnel Exemple fonctionnel
Description de la solution Description de la solution
Marche à suivre Marche à suivre
Pour aller plus loin… Pour aller plus loin…
Liens vers d’autres ressources : Liens vers d’autres ressources :

Exemple fonctionnel

Prenons l’exemple d’un formulaire tout simple (cf. Figure 1) de saisie d’une petite annonce, construit à l’aide du contrôle serveur FormView d’ASP.NET 2.0. Deux champs ‘Rubrique’ et ‘Catégorie’ de ce formulaire sont dépendants l’un de l’autre. La sélection dans la liste ‘Rubrique’ entraîne le changement du contenu de la liste ‘Catégorie’, chaque rubrique de petites annonces ayant ses propres catégories.


Figure 1 - Vue du contrôle FormView en mode d’insertion

Dans la base de données, la table [Annonces] ne contient pas évidemment de champ >nom< ni pour la rubrique ni pour la catégorie. Elle contient en réalité les ID (clés étrangères) des enregistrements correspondants dans les tables [Rubriques] et [Catégories].
Donc la première étape consiste à remplacer les ID par les noms et la seconde étape est de remplacer les zones de texte par des listes déroulantes pour proposer une aide à la saisie à l’utilisateur.

Description de la solution

Le procédé usuel est de définir, à l’intérieur du modèle InsertItemTemplate du contrôle d’affichage FormView, deux contrôles de source de données, un pour chaque liste déroulante de façon à charger les listes avec les données associées. Pour lier les deux listes entre elles, il suffit d’ajouter au contrôle de source de données de la liste enfant dépendante un paramètre dans la collection SelectParameters de type ControlParameter qui pointe sur la liste déroulante parent.

La liaison entre les deux listes est un procédé simple que vous pouvez appliquer sur différents types de contrôles d’affichage. Là où cela devient plus subtile, c’est que cette interaction entre deux contrôles intervient elle-même à l’intérieur d’un contrôle d’affichage avancé de données, tel que le FormView. Du coup, si on veut utiliser le databinding de manière déclarative, c’est plus délicat…
Du fait de l’imbrication des listes déroulantes, le databinding déclaratif paramétré sur la liste enfant ne fonctionne pas ! La solution consiste à faire l’équivalent du databinding déclaratif mais au lieu de le faire de manière déclarative, il faut le faire directement dans le code !

Marche à suivre

  1. Créez le formulaire et sa source de données en utilisant le mode Design de la page :

    • Ajoutez un contrôle de source de données de type SqlDataSource (ou ObjectDataSource si vous travaillez avec des objets métiers). Configurez le contrôle pour qu’il pointe vers votre source de données. Dans l’exemple, il gère la connexion à la table [Annonces].

    • Ajoutez un contrôle FormView > sa balise active > Choisir la source de données et pointez sur le contrôle de source de données.

    • Configurez le contrôle en mode d’insertion par défaut via ses propriétés > DefaultMode à la valeur Insert. Vous obtenez :

  2. Modifiez le modèle par défaut du contrôle FormView nommé InsertItemTemplate pour qu’il affiche deux listes déroulantes imbriquées :

    Remarques :
    - Dans cet exemple, nous allons nous concentrer sur le modèle d’insertion uniquement mais il faut penser à tous les autres modes éventuels d’utilisation de votre contrôle d’affichage tels que l’édition (EditItemTemplate) ou le mode en lecture simple (ItemTemplate).
    - Dans le cas d’un contrôle d’affichage de type GridView ou DetailsView, il faut sélectionner dans la balise active le menu Modifiez les colonnes (ou les champs) puis convertir le champ Rubrique et Catégorie en modèles avant de suivre la procédure suivante pour modifier le bon modèle.

    • Sélectionnez le contrôle FormView > sa balise active > Modifier les modèles puis sélectionnez le modèle InsertItemTemplate.

    • Remplacez les contrôles TextBox associés aux champs Id de la rubrique et Id de la catégorie par des contrôles DropDownList.

    • Ajoutez un contrôle de source de données de type SqlDataSource (ou ObjectDataSource) qui rapatrie les enregistrements de la table [Rubriques].

    • Ajoutez un second contrôle de source de données de type SqlDataSource (ou ObjectDataSource) qui rapatrie les enregistrements de la table [Catégories] en fonction de la rubrique correspondante.

    C’est là que vous allez relier les deux listes déroulantes entre elles ! La requête Select sur la table [Catégories] a une clause WHERE qui permet de ne rapatrier que les catégories pour une rubrique donnée.

    • Sélectionnez le contrôle de source de données des Catégories > Configurer la source de données puis choisissez la connexion à la base.

    • Dans la boîte Configurer l’instruction Select, sélectionnez Spécifiez les colonnes d’une table et pointez sur la table enfant [Catégories].

    • Cliquez sur le bouton Where… et configurez le paramètre de la requête Select pour qu’il récupère automatiquement la valeur sélectionnée dans le contrôle des Rubriques.

    • En mode Source, vous obtenez :

      <asp:SqlDataSource ID="SqlLstCategories" runat="server" 
              ConnectionString="<"%$ ConnectionStrings:csAffairesSansRisque %>"
           SelectCommand="SELECT * FROM [Categories] WHERE ([Rub_Id] = @Rub_Id)">
         <SelectParameters>
           <asp:ControlParameter ControlID="ddlRubriques" Name="Rub_Id" 
                  PropertyName="SelectedValue" Type="Int32" />
         </SelectParameters>
      </asp:SqlDataSource>
                    

      Remarque : Dans la mesure où les deux sources de données Rubriques et Catégories ne changent pas souvent, vous pouvez utiliser les propriétés de cache des contrôles SqlDataSource pour garder en mémoire les données affichées dans les listes et économiser ainsi le temps de chargement à partir de la base de données SQL Server.

    • Une fois les deux contrôles de sources de données configurés, attribuez les sources de données aux deux DropDownList. Paramétrez la valeur affichée dans la liste sur le champ Nom et la valeur à considérer dans la source de données sur le champ Id.

    Dernière étape et c’est là qu’intervient la notion de databinding déclaratif, vous devez indiquer à ASP.NET comment faire la liaison entre les valeurs sélectionnées dans les listes déroulantes et la source de données principale des Annonces associée au formulaire. Typiquement pour le champ Rubrique, cela revient à dire à ASP.NET de mettre à jour l’id de la rubrique dans l’enregistrement annonce à partir de la valeur sélectionnée (donnée par la propriété SelectedValue) dans la liste déroulante et inversement, si la source de données est amenée à changer, la liste déroulante doit afficher le champ correspondant.

  3. Configurez le databinding entre la source de données des [Annonces] et les contrôles d’affichage :

    • Dans la balise active de la DropDownList de la Rubrique, cliquez sur Modifier les Databindings… et ajoutez Bind(« Rub_Id ») pour la propriété SelectedValue :

    En théorie, il faudrait reproduire exactement la même configuration pour la valeur sélectionnée dans la liste déroulante enfant des catégories… …sauf que là, cela ne marche pas, la liste étant elle-même liée à la liste parent des Rubriques. A l’exécution, lorsque vous sélectionnez une rubrique dans la liste parent, vous récupériez l’erreur suivante :

    __________________________________________________________________________________________________________
    Les méthodes de liaison de données telles que Eval(), XPath() et Bind() peuvent uniquement être utilisées dans le contexte d'un contrôle lié aux données.

    Description : Une exception non gérée s'est produite au moment de l'exécution de la demande Web actuelle. Contrôlez la trace de la pile pour plus d'informations sur l'erreur et son origine dans le code.

    Détails de l'exception: System.InvalidOperationException: Les méthodes de liaison de données telles que Eval(), XPath() et Bind() peuvent uniquement être utilisées dans le contexte d'un contrôle lié aux données.
    __________________________________________________________________________________________________________

    Le problème vient du fait que la liste déroulante enfant se charge lorsque se déclenche l’évènement SelectedIndexChanged de la liste déroulante parent. A ce moment là, ASP.NET effectue le binding de la liste déroulante enfant mais ne refait pas celui du contrôle FormView qui est lui, rechargé à partir de l’état sauvegardé dans le ViewState. C’est là que cela ne colle plus !

    Certes c’est dommage, mais il faut faire avec... et c’est l’occasion de voir comment refaire l’équivalent par vous-même directement dans le code de la page !

    Le principe consiste à trapper le moment où à lieu l’insertion de la nouvelle annonce dans la source de données pour donner explicitement à ASP.NET la valeur du champ de la liste déroulante à insérer. A cet effet, le contrôle FormView a toute une batterie d’évènements qui se déclenchent lors des actions déclenchées sur la source de données.

    • A partir de la fenêtre de propriétés du contrôle FormView, générez une procédure de réponse à l’évènement ItemInserting.

    • Le code de récupération de la valeur de la catégorie sélectionnée est le suivant :

      Code VB.NET

                      Protected Sub FormView1_ItemInserting(ByVal sender As Object,
                        ByVal e As System.Web.UI.WebControls.FormViewInsertEventArgs)
                        Handles FormView1.ItemInserting
                        'Retrouver le contrôle formulaire.
                        'C'est lui qui a déclenché l'évènement ItemInserting.
                        Dim formulaire As FormView = CType(sender, FormView)
                        'Retrouver le contrôle de la liste déroulante des catégories.
                        'Il est encapsulé dans le modèle InsertItemTemplate du formulaire.
                        Dim listeDesCategories As DropDownList =  _
                        CType(formulaire.FindControl("ddlCategories"), DropDownList)
                        'Récupérer la valeur (correspondant à l'ID) de la catégorie sélectionnée
                        Dim strCategorie As String = listeDesCategories.SelectedValue
                        'Mettre à jour le buffer des données à insérer dans la source de données
                        'Utiliser le paramètre de l'évènement qui contient une collection des valeurs
                        'de l'enregistrement.
                        e.Values("Cat_Id") = Convert.ToInt32(strCategorie)
                        'Poursuivre l'action d'insertion normalement
                        e.Cancel = False
                      End Sub
                    

      Code C#

      protected void FormView1_ItemInserting(object sender, FormViewInsertEventArgs e)
      {
       //Retrouver le contrôle formulaire.
       //C'est lui qui a déclenché l'évènement ItemInserting.
       FormView formulaire = (FormView)sender;
       //Retrouver le contrôle de la liste déroulante des catégories.
       //Il est encapsulé dans le modèle InsertItemTemplate du formulaire.
       DropDownList listeDesCategories = (DropDownList)formulaire.FindControl("ddlCategories");
       //Récupérer la valeur (correspondant à l'ID) de la catégorie sélectionnée
       string strCategorie = listeDesCategories.SelectedValue;
       //Mettre à jour le buffer des données à insérer dans la source de données
       //Utiliser le paramètre de l'évènement qui contient une collection des valeurs
       //de l'enregistrement.
       e.Values["Cat_Id"] = Convert.ToInt32(strCategorie);
       //Poursuivre l'action d'insertion normalement
       e.Cancel = false;
      }
      
                    

    Remarque :
    Si votre formulaire contient des contrôles serveur de validation de données, vous pouvez entourer ce code d’une condition de type if(Page.IsValid)… pour éviter l’exécution du code si les contrôles sur la page n’ont pas été validés avec succès.

Pour aller plus loin…

Lorsque l’utilisateur sélectionne une rubrique dans la liste déroulante, l’intégralité de la page est postée sur le serveur. Cela se voit à l’œil nu puisqu’on observe un flash de la page qui est rechargée à partir de la réponse renvoyée par le serveur.
Pourtant il n’y aurait pas grand-chose à renvoyer au serveur…Poster toute la page revient à surcharger l’aller-retour sur le serveur inutilement. Il suffirait d’envoyer uniquement la valeur sélectionnée dans la liste des rubriques pour que le serveur charge le contenu de la liste des catégories avec les catégories correspondantes. C’est faisable grâce à la future technologie ASP.NET nommée Microsoft ASP.NET Ajax Extensions (en Beta 1).

Liens vers d’autres ressources :

© 2009 Microsoft Corporation. Tous droits réservés. Conditions d'utilisation | Marques | Confidentialité
Page view tracker