none
Composant et DataList : passage de paramètre RRS feed

  • Question

  • Bonjour tout le monde,

    J'ai écrit un contrôle utilisateur pour afficher les données d'un plat sur la carte d'un restaurant. Nous avons là-dessus un titre, un descriptif, une photo, deux interfaces de sélection du degré de cuisson dont l'une s'affiche en fonction d'un champ de la table, et encore quelques autres informations avec les prix.

    J'ai testé ce composant seul sur la page, y compris en y ajoutant le support du clavier (ce qu'il me faudra d'ailleurs affiner car les raccourcis clavier vont du coup se trouver en plusieurs exemplaires). Le composant a une propriété idPlat, qui lorsqu'il est seul sur la page apparaît dans la fenêtre des propriétés. Lors des tests j'y ai mis "en dur" dans la boîte de dialogue de propriétés le numéro d'un plat existant, et les données de ce plat se sont bien affichées. J'ai testé le changement d'image, de langue d'afffichage, de mode de cuisson.

    Maintenant arrive une phase plus intéressante : si nous voulons afficher une carte, il y faut plusieurs plats. J'ai donc mis mon composant dans l'ItemTemplate d'une DataList. Cette DataList est associée à une source de données qui retourne le champ id des enregistrements de la table Plat. Au demeurant j'ai restreint à une catégorie mais il ne me semble a priori pas que ce point aide à répondre à la question.

    Les propriétés définies dans le code du composant n'apparaissent plus dans la fenêtre des propriétés du composant, si celui-ci est sélectionné dans l'ItemTemplate du DataList. Je suis donc passé en mode source pour, dans l'ItemTemplate du DataList, retoucher le composant uc1:affichePlat, en insérant dans la balise qui le décrit :

    idPlat ='<%# Eval("id") %>'

    D'habitude c'est comme ça qu'on fait, non ?

    L'ItemTemplate contient deux contrôles :

    • un Label lié au champ id, qui affiche bien l'id que j'ai vu dans la table
    • affichePlat, qui a l'inconvénient de recevoir 0 comme idPlat.

    Ce que je viens de décrire fait-il apparaître une erreur grosse comme moi, ou faut-il que je donne plus de détails sur le composant, ou peut-être sur la DataList ? Par où commencer ?

    Le composant contient une DataSource pour renseigner certains éléments, d'autres sont renseignés par un appel à DAO dans le Page_Load du composant. Tout ce monde-là fonctionne bien en étant seul sur la page, mais dans le DataList reçoit 0 comme idPlat.

    Peut-être que ça ne gâchera rien de voir le code de la propriété dans le composant. Les propriétés sont public, il ne me semble pas qu'il y ait ambiguïté d'un exemplaire à l'autre du composant ?


        private int _idPlat;
        private string strCle;
        private int _maxCuisson=100;
        [Category("Parametres")]
        public int idPlat
        {
            get { return _idPlat; }
            set { 
                _idPlat = value;
                strCle = Cult.FormatCle("Plat", value.ToString());
            }
        }
    • Modifié Gloops dimanche 28 septembre 2014 16:00
    dimanche 28 septembre 2014 15:49

Réponses

  • Votre approche semble correcte (je parle du premier post).

    Concernant ManagedDTS & co, surtout oubliez, ca n'a rien à voir avec cela.Votre solution est sans doute bcp plus simple (sans doute un DataBind mal placé ou une erreur de syntaxe dans le Eval).

    Est-ce que vous pourriez poster le code de votre page (là ou ca fait le DataBind) et le code de votre page + celui de votre contrôle ?


    Richard Clark
    Consultant - Formateur .NET
    http://www.c2i.fr
    Depuis 1996: le 1er site .NET francophone

    • Marqué comme réponse Gloops mercredi 1 octobre 2014 16:17
    lundi 29 septembre 2014 05:08
  • Dans le DataList, pour déclencher un DataBind sur le composant, j'ai essayé ItemCreated, ItemDataBound et PreRender. J'aurais bien une préférence pour le deuxième, non ?

    Raté, c'était Page_Load(). Et d'ailleurs là (Page_Load de la page)  je mets DataList.DataBind(), ce qui déclenche automatiquement le rafraîchissement des composants qui sont dans la DataList.

    C'était bien plus facile à trouver sur une page de test séparée avec le strict nécessaire.

    Ensuite, s'est avéré que la liste déroulante de sélection de la catégorie de plats est plus délicate qu'il n'y paraît à mettre en œuvre, puisque le Page_Load du composant (situé dans la DataList) s'exécute avant le SelectedIndexChanged, et non après.

    De plus, si je demande explicitement un DataBind du composant à partir du SelectedIndexChanged, le composant n'est plus "databound", donc erreur mentionnée en 3 (voir dans le fil).

    J'ai modifié la DataSource de la DataList (celle qui fournit les numéros de plats), pour prendre son paramètre (le numéro de catégorie) non plus dans la DropDownList, mais dans la QueryString, ce qui au demeurant se rapproche de ce que j'avais l'intention de faire après.

    J'ai remis dans le composant son contenu d'origine à l'exception des variantes (plusieurs par plat), et ça marche.

    Gérer une autre source de données dans le composant ça devient une autre question, si ça s'avère ardu j'ouvrirai un autre fil.

    Encore merci pour le support.


    • Modifié Gloops jeudi 2 octobre 2014 09:00
    • Marqué comme réponse Gloops jeudi 2 octobre 2014 09:11
    jeudi 2 octobre 2014 08:59

Toutes les réponses

  • Une fois que je mets un DataBind() sur mon composant, la surbrillance se place sur la première moitié du code ASP.Net de définition du composant dans la page, et j'obtiens une exception qui me dit "Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control."

    C'est là que je dis Gloops, alors voici le code ASP.Net en question :

            <ItemTemplate>
                id:
                <asp:Label ID="idLabel" runat="server" Text='<%# Eval("id") %>'></asp:Label>
                <uc1:AffichePlat ID="AffichePlat2" runat="server" OnLoad="AffichePlat2_Load" idPlat='<%# Eval("id") %>'  />
            </ItemTemplate>

    Je vois le même code pour Text de Label, et lui est bien "databound". Qu'a-t-il bien pu arriver à mon composant pour ne pas être "databound" ?

    • Modifié Gloops dimanche 28 septembre 2014 16:58
    dimanche 28 septembre 2014 16:53
  • Ah, on dirait que je vais revenir après avoir lu ça :

    Creating a Path between Components

    dimanche 28 septembre 2014 17:12
  • Ah, on dirait que je vais revenir après avoir lu ça :

    Creating a Path between Components

    Cette piste implique l'utilisation de la référence ManagedDTS (using Microsoft.SqlServer.Dts.Runtime;)

    J'ai donc sélectionné ajouter une référence, parcourir, C:\Windows\Assembly, et là-dedans Microsoft.SQLServer.ManagedDTS

    et il m'a été répondu que cette DLL n'existait pas.

    http://social.msdn.microsoft.com/Forums/sqlserver/en-US/1b3ed694-cf02-445e-8ff4-0de4fd45a464/error-dts-does-not-exist-in-the-namespace-microsoftsqlserver?forum=sqlintegrationservices

    Il me faut donc préciser la configuration : SQL Express 2008 R2 + Visual Studio 2005 Professional, sur Windows XP SP3.

    ManagedDTS fait partie de SSIS, je crois me rappeler qu'il manque des billes sur cette machine pour pouvoir installer ça. Donc, si on veut suivre ça, la trajectoire serait plutôt terminer le projet pour gagner des sous avec, et changer de machine.

    J'imagine qu'un composant peut récupérer un champ d'un DataList sans que SSIS soit installé ? Ou qu'à défaut on peut se rabattre sur une autre solution ?

    J'avais pensé au Repeater, mais j'afficherais bien mes composants sur trois colonnes, et là la DataList aparaissait bien pratique.

    • Modifié Gloops dimanche 28 septembre 2014 18:37
    dimanche 28 septembre 2014 18:33
  • Votre approche semble correcte (je parle du premier post).

    Concernant ManagedDTS & co, surtout oubliez, ca n'a rien à voir avec cela.Votre solution est sans doute bcp plus simple (sans doute un DataBind mal placé ou une erreur de syntaxe dans le Eval).

    Est-ce que vous pourriez poster le code de votre page (là ou ca fait le DataBind) et le code de votre page + celui de votre contrôle ?


    Richard Clark
    Consultant - Formateur .NET
    http://www.c2i.fr
    Depuis 1996: le 1er site .NET francophone

    • Marqué comme réponse Gloops mercredi 1 octobre 2014 16:17
    lundi 29 septembre 2014 05:08
  • Bonjour,

    Il peut effectivement y avoir besoin d'extraire le code pour le publier.

    En attendant voici déjà de quoi soumettre à votre sagacité.

        protected void dnlCategorie_SelectedIndexChanged(object sender, EventArgs e)
        {
            dsPlats.DataBind();
            try
            {
                DataList1.DataBind();
            }
            catch (System.Reflection.CustomAttributeFormatException cex)
            {
                System.Diagnostics.Debug.Print(cex.ToString());
            }
            catch (FormatException fex)
            {
                System.Diagnostics.Debug.Print(fex.Data.Keys.Count.ToString());
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Print(ex.Message);
            }
        }


    J'obtiens une FormatException, avec l'intitulé "Input string was not in a correct format".

    J'ai peine à croire que je ne trouve pas cette input string quelque part dans les paramètres de la FormatException, ce qui pourrait me mettre sur la voie de ce que j'ai improprement cherché à convertir.

    En principe, la documentation de base de cette exception devrait dire dans quelle propriété de l'exception on cherche ça, non ? Alors j'ai eu la berlue, car je n'ai pas vu ça dans la doc.

    Ce que j'ai mis ci-dessus affiche 0.

    Ah oui une précision : j'ai mis des gestions d'erreur dans toutes les procédures du module de la page et de celui du composant, et rien n'est levé de ce côté.

    On dirait qu'il va falloir bien regarder le code ASP.Net 

        <asp:DataList ID="DataList1" runat="server" DataKeyField="id" DataSourceID="dsPlats" OnItemDataBound="DataList1_ItemDataBound" RepeatColumns="2" OnPreRender="DataList1_PreRender" OnItemCreated="DataList1_ItemCreated">
            <ItemTemplate>
                id:
                <asp:Label ID="idLabel" runat="server" Text='<%# Eval("id") %>' OnDataBinding="idLabel_DataBinding" OnPreRender="idLabel_PreRender"></asp:Label>&nbsp;
                <asp:ListBox ID="lbx1" runat="server" Height="17px" SelectedValue='<%# Eval("id") %>' DataSourceID="dsPlats" DataTextField="id" DataValueField="id"></asp:ListBox>
                <uc1:AffichePlat ID="AffichePlat1" runat="server" Visible="true" />
            </ItemTemplate>
        </asp:DataList>&nbsp;

    L'exception mentionnée ci-dessus n'est pas levée si j'enlève le composant AffichePlat de l'ItemTemplate. En revanche bien entendu je me retrouve alors par endroits avec objet non implémenté (ça se dit comment déjà, Object not set to an instance of an object).


    • Modifié Gloops mardi 30 septembre 2014 15:46
    mardi 30 septembre 2014 15:35
  • Voici qui devrait éclairer utilement la question.

    Si j'ajoute à la balise uc1:AffichePlat l'attribut

    idPlat="13"

    alors mon steack, qui pour le moment est seul dans la table, s'affiche proprement.

    Avec toutefois l'inconvénient, en toute logique me semble-t-il, que si j'ai 15 plats dans la table je vais afficher 15 steacks, ce qui risque de ne pas être du goût du client.

    L'exception apparaît à nouveau si je mets

    idPlat='<%# Eval("id").ToString() %>'

    et ce, avec ou sans ToString(). Pourtant, le Label au-dessus affiche bien 13.

    Je précise que, pour ne plus me faire dire que Int32 ne peut pas être créé à partir d'une chaîne de caractères, la propriété idPlat est devenue :

        [Category("Parametres")]
        public string idPlat
        {
            get { return _idPlat.ToString(); }
            set {
                try
                {
                    _idPlat = int.Parse(value);
                    strCle = Cult.FormatCle("Plat", value);
                }
                catch
                {
                    _idPlat = 0;
                }
            }
        }
    
    et je progresse là-dedans en pas à pas sans rencontrer d'exception.
    L'inconvénient de ceci que c'est davantage capillotracté, mais l'avantage est qu'effectivement, on ne me dit plus que Int32 ne peut pas être obtenu à partir d'une chaîne de caractères.

    Bon, maintenant, je vais afficher le contenu du champ "id" caractère par caractère, parce que j'ai l'impression que si il trimballe un séparateur ça risque de ne pas faciliter l'opération Parse.


    • Modifié Gloops mardi 30 septembre 2014 16:24
    mardi 30 septembre 2014 15:59
  • Bonjour,

    Bon là je me trouve un peu bête : la nuit dernière j'ai réussi à afficher le haut du composant avec les bonnes données, puis je me suis mélangé les pinceaux quand j'ai voulu faire apparaître le reste, et je ne réussis plus à obtenir ce que j'ai obtenu la nuit dernière.

    Au passage, merci à Richard qui m'a évité une grosse déviation. Sa réponse s'est confirmée être la bonne, dommage que j'aie perdu mes traces depuis.

    Voici ce que j'ai appris au cours de cette séance :

    • selon le moment auquel on applique un DataBind au composant, on peut obtenir trois résultats différents :
    1. Object not set to an instance of an object
    2. Mise à jour correcte
    3. Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.

    (et ce, sauf erreur de ma part, dans l'ordre d'"entrée en scène")

    • l'affichage du contenu du composant est obtenu au moment où on lui applique un DataBind. Pour capturer une exception telle que la 3 ci-dessus, c'est donc le DataBind qu'il faut placer dans un try/catch

    La FormatException se produisait à l'intérieur du composant. Celui-ci traite en fait trois sources de données, et sans gestion d'erreur il suffit que l'une d'elles rencontre un problème (dans un passage de paramètre ou autre) pour que le composant lève une exception.

    A présent j'ai réduit le composant à sa plus simple expression (en gardant bien sûr une copie de ce que j'avais au départ), et je tente de lui faire afficher le numéro de plat.

    La page n'est plus en erreur car j'ai mis tous les DataBind dans des try/catch, mais le composant n'affiche pas le numéro.

    Dans le DataList, pour déclencher un DataBind sur le composant, j'ai essayé ItemCreated, ItemDataBound et PreRender. J'aurais bien une préférence pour le deuxième, non ?

    Ou c'est encore ailleurs qu'il faut le mettre ?

    Je vais regarder à nouveau dans le composant si je n'aurais pas laissé traîner une erreur.

        protected void Page_Load(object sender, EventArgs e)
        {
            if (idPlat == "0") return;
            this.lblDesc.Text = idPlat;
        }

    Si il y a une erreur là-dedans elle va être facile à voir, non ?

    Quelque chose qui peut servir : comme clause idPlat dans la balise du composant sur la page, j'ai mis

    idPlat='<%# convid(Eval("id").ToString()) %>'

    ce qui renvoie à

        protected int convid(string entree)
        {
            System.Diagnostics.Debug.Print(entree);
            int val = int.Parse(entree);
            numPlat = val;
            return (val);
        }
    
    ça permet de faire une conversion, mais surtout, l'intérêt est que ça permet d'y mettre un point d'arrêt, comme ça on peut vérifier quelle donnée on passe au composant, et en pas à pas on passe sur la balise et on arrive sur les propriétés.

    • Modifié Gloops mercredi 1 octobre 2014 16:13
    mercredi 1 octobre 2014 16:08
  • Dans le DataList, pour déclencher un DataBind sur le composant, j'ai essayé ItemCreated, ItemDataBound et PreRender. J'aurais bien une préférence pour le deuxième, non ?

    Raté, c'était Page_Load(). Et d'ailleurs là (Page_Load de la page)  je mets DataList.DataBind(), ce qui déclenche automatiquement le rafraîchissement des composants qui sont dans la DataList.

    C'était bien plus facile à trouver sur une page de test séparée avec le strict nécessaire.

    Ensuite, s'est avéré que la liste déroulante de sélection de la catégorie de plats est plus délicate qu'il n'y paraît à mettre en œuvre, puisque le Page_Load du composant (situé dans la DataList) s'exécute avant le SelectedIndexChanged, et non après.

    De plus, si je demande explicitement un DataBind du composant à partir du SelectedIndexChanged, le composant n'est plus "databound", donc erreur mentionnée en 3 (voir dans le fil).

    J'ai modifié la DataSource de la DataList (celle qui fournit les numéros de plats), pour prendre son paramètre (le numéro de catégorie) non plus dans la DropDownList, mais dans la QueryString, ce qui au demeurant se rapproche de ce que j'avais l'intention de faire après.

    J'ai remis dans le composant son contenu d'origine à l'exception des variantes (plusieurs par plat), et ça marche.

    Gérer une autre source de données dans le composant ça devient une autre question, si ça s'avère ardu j'ouvrirai un autre fil.

    Encore merci pour le support.


    • Modifié Gloops jeudi 2 octobre 2014 09:00
    • Marqué comme réponse Gloops jeudi 2 octobre 2014 09:11
    jeudi 2 octobre 2014 08:59