IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VB.NET Discussion :

Fonction de remplissage de contrôles


Sujet :

VB.NET

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut Fonction de remplissage de contrôles
    Bonjour

    Suite à ce topic (filtrage-multi-critere-dataviews-plusieurs-evenements) où on m'avait donné des éléments de réponse pour construire une fonction de remplissage, je cherche à optimiser les 2 boucles qui ont un inconvénient visuel (effet sapin de Noël clignotement des contrôles).
    Le code du topic précédent a donc reçu quelques optimisations grâce aux conseils du cours de P.Lasserre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
        Private Sub UpdateEquipList(ByVal flpanel1 As FlowLayoutPanel, Optional ByVal flpanel2 As FlowLayoutPanel = Nothing, Optional ByVal flpanel3 As FlowLayoutPanel = Nothing, Optional ByVal Setup As String = "", Optional ByVal RowFilter As String = "")
            Dim dv As DataView
            Dim NeededDataTables As String() = {"Food", "Weapon", "Ranged", "Ammo", "Head", "Neck", "Ear", "Ring", "Hands", "Body", "Legs", "Feet", "Waist", "Back", "WStable"}
            If {"", "1", "2"}.Contains(Setup) AndAlso flpanel1 IsNot Nothing Then
                For Each flpanel As FlowLayoutPanel In New Control() {flpanel1, flpanel2, flpanel3}
                    For Each dt As DataTable In Me.DataSet.Tables
                        If NeededDataTables.Contains(dt.TableName) Then
                            For Each Cbx As ComboBox In flpanel.Controls.OfType(Of ComboBox)().Where(Function(Cbx) Cbx.Name.Contains(dt.TableName))
                                If Setup = "" OrElse Cbx.Name.Contains(Setup) Then
    
                                    dv = New DataView(dt)
                                    dv.RowFilter = RowFilter
                                    dv.RowStateFilter = DataViewRowState.CurrentRows
                                    dv.ApplyDefaultSort = True
                                    Cbx.DataSource = dv
                                    Cbx.DisplayMember = "Names"
    
                                End If
                            Next
                        End If
                    Next
                Next
            End If
        End Sub
    En français ce code signifie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Fonction UpdateEquipList de Paramètres(Conteneur(s)?, Tous les controles ou juste une partie ?, expression de filtrage)
    Pour chaque Conteneur spécifié dans Paramètres
         Pour chaque DataTable importée (liste préfiltrée)
              Pour chaque contrôle dans le conteneur en cours ET dont le nom contient le nom de la DataTable en cours
              Faire
              ...
              Controle.Datasource = Dataview filtrée avec Expression spécifiée dans Paramètres
              ...
              Suivant
         Suivant
    Suivant
    Sur mon interface j'ai 3 conteneurs de type flowlayoutpanel, chacun contenant 2 à 30 comboboxs et certains ont 2 listboxs. Les listboxs je les remplis "à la main" ailleurs... donc là ma fonction ne se préoccupe que des comboboxs.

    Dans ce code j'ai réussi à limiter le nombre de bouclages de la 1ère boucle grâce à une liste de strings. Par contre pour la 2e boucle...

    Mon problème concerne en particulier les lignes en gras, càd que je n'arrive pas à construire une [liste de contrôles] sans que VB2010 me foute une erreur de syntaxe ou une exception d'Argument à l'execution...
    En fait j'essaie d'adapter l'exemple de Krosoft sur MSDN...
    L'erreur affichée par VB pour la syntaxe du code ci-dessus est :
    "Le paramètre lambda 'Cbx' masque une variable dans un bloc englobant, une variable de portée précédemment définie ou une variable déclarée implicitement dans une expression de requête."
    Si quelqu'un peut m'aider à communiquer correctement avec VB, ce serait svp.
    Les suggestions d'implémentation sont aussi les bienvenues ^^

  2. #2
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    je ne sais pas qui t'as conseillé ce code mais il est illisible ; des sous méthodes ou du linq aideraient à la lisibilité

    sinon ton erreur vient de là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     For Each Cbx As ComboBox In flpanel.Controls.OfType(Of  ComboBox)().Where(Function(Cbx) Cbx.Name.Contains(dt.TableName))

    for each cbx déclare une variable cbx puis tu dis function (cbx) qui en déclare une autre du meme nom

    =>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    For Each Cbx As ComboBox In flpanel.Controls.OfType(Of  ComboBox)().Where(Function(Cbx2) Cbx2.Name.Contains(dt.TableName))

    edit : je détaille comment rendre ce genre de chose plus lisible, pas forcément calqué sur ton cas mais sub des boucles et sous boucles :

    à la place de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for a 
      for b
        for c
        traitmement sur c

    tu peux faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    sub départ
      for a
         traitementsurtypedea(a)
     
    sub traitementsurtypedea(a)
      for b
         traitementsurtypedeb(b)
     
    sub traitmentsurtypedeb(b)
      traitement sur c
    comme ca les boucles sont vides et une sub contient le code du traitement

    et en linq ca doit donner un truc du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    from a in listedeA from b in a.listedeB from c in listedeC where condition sur c.for each (addressof traitementsurtypedeC)(c)
    donc une ligne et une sub
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    Comment ça illisible ? Aurais-tu une meilleure idée que 2 simples boucles ForEach imbriquées ?
    Je suis débutant, je ne sait pas ce que tu veux dire par "sous-méthodes", où ca dans le code il y en aurait besoin ? Idem pour Linq, jamais utilisé

    Quant à l'erreur j'ai fait ce que tu m'as dit cela enlève l'erreur mais rajoute un avertissement
    l'utilisation de la variable d'itération dt peut provoquer des résultats inattendus...
    Je l'ai ignoré et lancé le déboguage et là VLAN! InvalidOperationException
    Je reçois la même Exception si je remplace la var dt par dt2 ou CurrentDT=New Datatable(dt.tablename)...

    Le pire est que d'habitude quand une exception est levée VB2010 me pointe automatiquement où elle se trouve en la surlignant. Là rien du tout, écran blanc... Donc je ne vois pas ce qui provoque réellement cette exception ??

  4. #4
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    pour l'avertissement ca vient de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    flpanel.Controls.OfType(Of ComboBox)().Where(Function(Cbx) Cbx.Name.Contains(dt.TableName))

    c'est du linq et linq n'aime pas qu'on lui fasse manipuler des variables qui proviennent d'un for each

    il faut faire juste avant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    dim dt2 as datatable = dt
    et mettre dt2 dans l'expression linq

    en espérant que ca retire l'exception au passage (surment le cas car je crois que les exception dans du linq ne font pas pointer vers la ligne)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    Bonjour Pol63 et merci pour tes réponses

    Nos posts ont du se croiser hier soir, mais le probleme du dt2 ne leve toujours pas l'InvalidArgumentException... (plus d'avertissement par contre)

    J'ai passé 1heure a decortiquer la fenetre InvalidArgumentException pour "essayer" de savoir quelle foutue ligne de code ma faisait planter... rien trouvé... Pourtant il me parle de mettre un "New" pour instancier un objet quelquepart...

    Bon jme suis dit o point j'en suis... autant m'essayer au linq comme tu me l'as laissé entendre dans ton post. Il y a en effet un chapitre dessus dans le cours de P.Lasserre, et tous ses exemples utilisent clairement un language de requete avec au minimum les instructions FROM et In.
    En passant, mon code n'ayant pas de FROM du tout, je vois pas à quoi tu lui trouve une ressemblance même lointaine à du Linq ?
    Je me tente donc a réécrire 2 des 3 boucles précédentes "correctement" en suivant tes conseils (faire sauter b et c en une seule boucle) et la syntaxe du cours Lasserre, ce qui donne ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        Private Sub UpdateEquipList(ByVal flpanel1 As FlowLayoutPanel, Optional ByVal flpanel2 As FlowLayoutPanel = Nothing, Optional ByVal flpanel3 As FlowLayoutPanel = Nothing, Optional ByVal Setup As String = "", Optional ByVal RowFilter As String = "")
            Dim dv As DataView
            Dim NeededDataTables As String() = {"Food", "Weapon", "Ranged", "Ammo", "Head", "Neck", "Ear", "Ring", "Hands", "Body", "Legs", "Feet", "Waist", "Back", "WStable"}
            Dim OriginalRowFilter As String = RowFilter
            Dim MutualExCbxName, MutualExCbxText As String
            If {"", "1", "2"}.Contains(Setup) AndAlso flpanel1 IsNot Nothing Then
                For Each flpanel As FlowLayoutPanel In New Control() {flpanel1, flpanel2, flpanel3}
                    Dim dt2 As DataTable
                    Dim ComboboxList As Control() = From Cbx2 In flpanel.Controls.OfType(Of ComboBox)() Where Cbx2.Name.Contains( _
                                                    (From dt2 In Me.DataSet.Tables Where NeededDataTables.Contains(dt2.TableName)).TableName)
                    For Each Cbx As ComboBox In ComboboxList
    Je n'ai pas tenter de déboguer car il y a encore une erreur sur le dt2, malgré tes conseils VB n'aime toujours pas...
    L'erreur est :
    "La variable de portée 'dt2' masque une variable dans un bloc englobant ou une variable de portée précédemment définie dans l'expression de requête."

  6. #6
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    ton erreur vient de :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Dim dt2 As DataTable
    Dim ComboboxList As Control() = From Cbx2 In flpanel.Controls.OfType(Of ComboBox)() Where Cbx2.Name.Contains( _
                                                    (From dt2 In Me.DataSet.Tables Where NeededDataTables.Contains(dt2.TableName)).TableName)
    le from s'écrit au complet sous cette forme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    from var as type in collection
    il faut voir ca comme un dim var as type puis un for each var in collection
    si le as type n'est pas spécifié, ca équivaut à as Object
    var est donc une variable déclarée pour le bloc from, utilisé par linq pour parcourir la collection

    tu te retrouves donc avec 2 variables dt2 au meme moment


    je vais regarder ton code un peu plus en détail pour comprendre ce que tu veux faire et voir si ca peut s'écrire autrement


    et pour info linq ce n'est pas seulement basé sur from, linq est un langage fonctionnel au sein de .net
    fonctionnel veut dire qu'on décrit ce qu'on veut faire sous forme de "phrase" et non de code, la compilation s'occupant de ca
    si on a une collection de control, et qu'on veut les controle ayant un a dans leur nom en linq ca donne ca:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    dim controlsAvecUnA as ienuerable(of control)
    controlsAvecUnA  = me.Controls.where(function ....)
    le .where c'est aussi du linq, on lui donne une fonction (anonyme ou non) et il l'exécute pour chaque membre de la collection de départ

    les méthodes/fonctions anonymes sont comme des fonction normales, sauf qu'elle n'ont pas de nom, sont écrites au sein d'un autre bloc et on peut avoir un pointeur vers elle
    en théorie ca permet de remplacer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    private function sum(a as integer, b as integer) as integer
      return a+b
    end function
    en ca : (je tape direct ici, donc la syntaxe est peut etre pas exacte)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    dim f as new func(of integer, integer, integer)(function (var1 as integer, var2 as integer)(a+b))
    dim result as integer = f.invoke(51,32)
    le integer, integer, integer veut dire qu'il y a 2 paramètres integer en entrée et un integer en sortie, le dernier type est le type de sortie, il peut etre seul s'il n'y a pas de paramètre d'entrée
    le (a+b) est le code de la fonction, il faut imaginer un return devant, mais qui ne s'écrit pas dans le cas d'une fonction anonyme
    ca peut parraitre inutile comme ca, mais les cas ou c'est utile c'est au cas ou on ne connait pas à l'avance quelle fonction va etre utilisée
    par exemple, une liste de nombre, un textbox de saisie de nombre et un combobox qui contient < = >
    on fait une fonction anonyme qui retourne les inférieurs de la liste par rapport au nombre, une pour les supérieurs etc...
    et selon ce que l'utilisateur choisit dans le combobox on invoke la variable fonction sur la liste


    je ne sais pas si je suis clair, mais .net est un langage très concret, qui ne laisse pas de place au hasard (contraitement à vb6 par exemple ) et qu'il faut comprendre en détail pour coder proprement et efficacement, diminuant le temps de développement et facilitant la maintenance du code
    et certaines parties nécessitent des exemples utiles pour comprendre leur utilité
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    Ok merci pour tes explications, je comprends mieux pourquoi mes 2 "dt2" foutaient la m...

    Mais je crois que cette histoire de Linq pose (ou va poser) un autre probléme qui remet en cause toute la cocneption de la fonction.
    En effet jusqu'a maintenant je bossait sur le principe d'affecter aux Datasource des comboboxs une dataview filtrée grâce un string RowFilter.
    Ce qui est cool avec ce principe est que je peut vraiment personnaliser le RowFilter (qui est en parametre d'ailleurs).

    En revanche si on se lance dans du Linq, je ne suis pas sûr si on peut retrouver le même principe... ?

    Ce qui me fait dire ca, c'est quand j'ai fait sauter la boucle for each dt... VB m'a souligné la ligne en dessous " 'dv As Dataview = New DataView(dt)' est foireuse". Mais peut être je me fais de la bile pour rien

  8. #8
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    voici un code totalement différent, le but n'est pas forcément que tu le reprennes, même si j'espère qu'il fait la même chose
    le but c'est de voir d'autres approches, et de les comprendre
    et tu peux aussi être contre ce que je propose, c'est une question d'opinion


    les différences par rapport à ton code :
    - moins d'imbrications, les if et for décalent le code le rendant illisible quand il y en a trop ; remplacer des if then traitment end par des if not then exit sub traitement, ca fait un imbrication de moins
    - tu recherches des combobox dans plusieurs flowlayoutpanels, plutot que d'imbriquer 2 if, avec du linq tu peux les obtenir directement tous dans une collection (variable comboboxes)
    - tu parcours toutes les datatables du dataset et à chaque tour tu regarde si le datatable est contenu dans ton tableau de string ; autant te faire une collection des datatables utiles directement, pour ne parcourir que celle là
    - j'ai les combobox et les datatables, je créé ensuite une collection d'une classe anonyme avec chaque combobox et le datable qui lui est lié
    une classe anonyme ca s'écrit avec new with {.p1=valeur,.p2=valeur}, ce qui équivaut à une classe ayant une propriété p1 et une propriété p2, pratique pour regrouper des choses ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    Private Sub UpdateEquipList(ByVal flpanel1 As FlowLayoutPanel, Optional ByVal flpanel2 As FlowLayoutPanel = Nothing, Optional ByVal flpanel3 As FlowLayoutPanel = Nothing, Optional ByVal Setup As String = "", Optional ByVal RowFilter As String = "")
        Dim NeededDataTables As String() = {"Food", "Weapon", "Ranged", "Ammo", "Head", "Neck", "Ear", "Ring", "Hands", "Body", "Legs", "Feet", "Waist", "Back", "WStable"}
     
        If Not {"", "1", "2"}.Contains(Setup) Then Exit Sub
        If flpanel1 Is Nothing Then Exit Sub
     
        Dim DataTablesUtiles As IEnumerable(Of DataTable) = (From dt As Object In Me.DataSet.Tables _
                                                    Where NeededDataTables.Contains(DirectCast(dt, DataTable).TableName) _
                                                    Select DirectCast(dt, DataTable))
        'contient les datatables du dataset qui ont un nom de la liste
     
     
     
        Dim comboboxes As IEnumerable(Of ComboBox) = (From flp As FlowLayoutPanel In New FlowLayoutPanel() {flpanel1, flpanel2, flpanel3} _
                                                        Where flp IsNot Nothing
                                                        From c As ComboBox In flp.Controls.OfType(Of ComboBox)() Select c)
        'parcours les flp, s'ils ne valent pas nothing, parcours ensuite les controls de type combobox de chaque flp pour les ranger dans une collection
     
        Dim CbxEtDtALier = (From dt As DataTable In DataTablesUtiles _
                            From c As ComboBox In comboboxes _
                            Where (c.Name.Contains(Setup) OrElse Setup = "") And c.Name.Contains(dt.TableName) _
                            Select New With {.cbx = c, .dtble = dt})
     
        For Each element In CbxEtDtALier
            Dim dv As New DataView(element.dtble) ' dv n'est utile qu'ici donc ca "pollue" de le déclarer plus haut avec plus de portée
            dv.RowFilter = RowFilter
            dv.RowStateFilter = DataViewRowState.CurrentRows
            dv.ApplyDefaultSort = True
            element.cbx.DataSource = dv
            element.cbx.DisplayMember = "Names"
        Next
    End Sub
    y a même moyen de faire plus court ...
    et en fait ton code de départ était surtout illisible car tu as mis de la mise en forme au sein de la balise code, ce qui la rend inopérante et il nous manque la colorisation syntaxique
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    - moins d'imbrications, les if et for décalent le code le rendant illisible quand il y en a trop ; remplacer des if then traitment end par des if not then exit sub traitement, ca fait un imbrication de moins
    - tu recherches des combobox dans plusieurs flowlayoutpanels, plutot que d'imbriquer 2 if, avec du linq tu peux les obtenir directement tous dans une collection (variable comboboxes)
    - tu parcours toutes les datatables du dataset et à chaque tour tu regarde si le datatable est contenu dans ton tableau de string ; autant te faire une collection des datatables utiles directement, pour ne parcourir que celle là
    - j'ai les combobox et les datatables, je créé ensuite une collection d'une classe anonyme avec chaque combobox et le datable qui lui est lié
    C'est exactement les raisonnements que j'avais quand j'ai commencé cette fonction.
    J'en etait arrivé au code que tu vois dans mon post initial à cause de mon ignorance en termes de compatibilité de conversion (j'ai option Strict On) entre types, et aussi pour "fabriquer" des collections je dirait... "spécialisées". Donc forcément à force de "bidouillages" j'ai fini par obtenir quelquechose d'a peu près "propre" pour VB (qui leve pas ou peu d'exceptions/warnings), mais surement pas "propre" du point de vue implémenteur et performance.

    D'autre part, j'ai découvert que "Controls" a une méthode Find() qui permet de recup tous les contrôles enfants, tout en filtrant selon leur propirété control.name. Pourrait etre interessant à utiliser ? (je ne sait pas comment le combiner avec le filtrage .OfType(Of Combobox) utilisé actuellement)


    Je regarde now ta proposition de code ... c'est exactement ce que je cherche à obtenir. Honnetement je n'aurait jamais pu trouver cette syntaxe tout seul (surtout les DirectCast et le linq). Trop Merci !!
    Sinon, le Dim dv, en fait j'avais mis le Dim en haut de la fonction pour le sortir des boucles, ce qui fait une instruction de moins a faire a chaque tour. Ca parait effectivement "polluant" a la lecture, mais peut etre + rapide a executer ?
    Le Where flp IsNot Nothing sert a rien ? (y a deja un If controlant ca + haut non ?
    Je posterai le code final basé sur ta proposition

  10. #10
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Citation Envoyé par Masamunai Voir le message
    j'ai option Strict On
    option strict on c'est bien déjà
    ca implique d'utiliser des directcast assez souvent
    quand on a une variable as quelquechose et qui contient quelque chose que dont tu es sûr et de type sous jacent(=héritant) c'est nécessaire (tout est hérite de object, combobox hérite de control etc...)

    Citation Envoyé par Masamunai Voir le message
    qui leve pas ou peu d'exceptions/warnings), mais surement pas "propre" du point de vue implémenteur et performance.
    à ce propos, je préconise aussi de mettre des try catch le plus possible, et d'enregistrer les erreurs (ex.exception et ex.stacktrace au moins) ce qui permet de débugger facilement une fois en production

    Citation Envoyé par Masamunai Voir le message
    D'autre part, j'ai découvert que "Controls" a une méthode Find() qui permet de recup tous les contrôles enfants, tout en filtrant selon leur propirété control.name. Pourrait etre interessant à utiliser ?
    peut etre oui

    Citation Envoyé par Masamunai Voir le message
    Sinon, le Dim dv, en fait j'avais mis le Dim en haut de la fonction pour le sortir des boucles, ce qui fait une instruction de moins a faire a chaque tour. Ca parait effectivement "polluant" a la lecture, mais peut etre + rapide a executer ?
    pas sûr ^^, au mieux ca gagne une nanoseconde (10^-9) et puis de toute facon le code compilé en code machine ne ressemble en rien à ce que tu écris, c'est optimisé
    par contre limiter la portée des variables c'est utile, une variable déclarée dans un for n'existe plus hors du for (sortie de bloc), tout comme une variable déclarée dans linq n'existe pas hors du linq

    Citation Envoyé par Masamunai Voir le message
    Le Where flp IsNot Nothing sert a rien ? (y a deja un If controlant ca + haut non ?
    non, je teste juste comme toi que le flp1 isnot nothing, dans le from les 3 flp vont passer, mon where sert à éviter un plantage
    car si flp2 faut nothing le flp.controls de la ligne d'après équivaudra à nothing.controls et nothing.quelquechose plante forcément (NullReferenceException)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    Voilà le code adapté :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
        ''' <summary>Function updating dropdown lists of comboboxs inside specified container(s).
        ''' The function will update only comboboxs whose name contains their associated DataTable's name.
        ''' The user can add personalized filtering parameters.</summary>
        ''' <param name="flpanel1">1st group of comboboxs, necessary, As FlowLayoutPanel</param>
        ''' <param name="flpanel2">2nd group of comboboxs, optional, As FlowLayoutPanel</param>
        ''' <param name="flpanel3">3rd group of comboboxs, optional, As FlowLayoutPanel</param>
        ''' <param name="Setup">Number designating which Setup belongs the comboboxs to be reseted, value As String, optional, accepted values = "", "1" or "2"</param>
        ''' <param name="RowFilter">Additional filtering parameters, optional, as String</param>
        ''' <remarks>The function will keep current text values in comboboxs if they already match the filter expression.</remarks>
        Private Sub UpdateEquipList(ByVal flpanel1 As FlowLayoutPanel, Optional ByVal flpanel2 As FlowLayoutPanel = Nothing, Optional ByVal flpanel3 As FlowLayoutPanel = Nothing, Optional ByVal Setup As String = "", Optional ByVal RowFilter As String = "")
            Dim OriginalRowFilter As String = RowFilter
            Dim MutualExCbxName, MutualExCbxText As String
            Dim NeededDataTables As String() = {"Food", "Weapon", "Ranged", "Ammo", "Head", "Neck", "Ear", "Ring", "Hands", "Body", "Legs", "Feet", "Waist", "Back", "WStable"}
     
            If Not {"", "1", "2"}.Contains(Setup) OrElse flpanel1 Is Nothing Then Exit Sub
     
            'Contains datatables from the Dataset, whose names appears in NeededDataTables
            Dim DataTablesToUse As  _
            IEnumerable(Of DataTable) = (From dt As Object In Me.DataSet.Tables _
                                         Where NeededDataTables.Contains(DirectCast(dt, DataTable).TableName) _
                                         Select DirectCast(dt, DataTable))
     
            'Searches inside specified container(s) for controls of type combobox,
            'Then builds a filtered collection with each row containing associated Datatable|Container|Combobox
            Dim CbxEtDtALier = (From dt As DataTable In DataTablesToUse _
                                From flp As FlowLayoutPanel In New FlowLayoutPanel() {flpanel1, flpanel2, flpanel3} _
                                Where flp IsNot Nothing _
                                From c As ComboBox In flp.Controls.OfType(Of ComboBox)() _
                                Where c.Name.Contains(dt.TableName) AndAlso (c.Name.Contains(Setup) OrElse Setup = "") _
                                Select New With {.dtble = dt, .flp = flp, .cbx = c})
     
            For Each element In CbxEtDtALier
     
                MutualExCbxName = ""
                Select Case element.cbx.Name ' All different pairs of mutually exclusive comboboxs
                    Case "MainWeapon1"
                        MutualExCbxName = "SubWeapon1"
                    Case "SubWeapon1"
                        MutualExCbxName = "MainWeapon1"
                    Case "LeftEarTP1"
                        MutualExCbxName = "RightEarTP1"
                    Case "RightEarTP1"
                        MutualExCbxName = "LeftEarTP1"
                    Case "LeftRingTP1"
                        MutualExCbxName = "RightRingTP1"
                    Case "RightRingTP1"
                        MutualExCbxName = "LeftRingTP1"
                    Case "LeftEarWS1"
                        MutualExCbxName = "RightEarWS1"
                    Case "RightEarWS1"
                        MutualExCbxName = "LeftEarWS1"
                    Case "LeftRingWS1"
                        MutualExCbxName = "RightRingWS1"
                    Case "RightRingWS1"
                        MutualExCbxName = "LeftRingWS1"
                    Case "MainWeapon2"
                        MutualExCbxName = "SubWeapon2"
                    Case "SubWeapon2"
                        MutualExCbxName = "MainWeapon2"
                    Case "LeftEarTP2"
                        MutualExCbxName = "RightEarTP2"
                    Case "RightEarTP2"
                        MutualExCbxName = "LeftEarTP2"
                    Case "LeftRingTP2"
                        MutualExCbxName = "RightRingTP2"
                    Case "RightRingTP2"
                        MutualExCbxName = "LeftRingTP2"
                    Case "LeftEarWS2"
                        MutualExCbxName = "RightEarWS2"
                    Case "RightEarWS2"
                        MutualExCbxName = "LeftEarWS2"
                    Case "LeftRingWS2"
                        MutualExCbxName = "RightRingWS2"
                    Case "RightRingWS2"
                        MutualExCbxName = "LeftRingWS2"
                    Case Else
                End Select
                If MutualExCbxName <> "" Then
                    MutualExCbxText = element.flp.Controls.Item(MutualExCbxName).Text
                    If MutualExCbxText <> "" Then
                        With element.dtble.Rows.Find(MutualExCbxText)
                            'Checking if the text of next Mutual Exclusive combobox will be reset or not when will be its turn. If yes, no RowFilter, otherwise filter it out this combobox's list.
                            If MutualExCbxName.StartsWith("Sub|Right") OrElse (.Item("Equippable Jobs").ToString.Contains(Me.MainJob1.Text & "|ALL") AndAlso CInt(.Item("Level").ToString) <= Me.MainLevel1.Value) Then
                                RowFilter = RowFilter & "AND NOT( Names = '" & Replace(MutualExCbxText, "'", "''") & "' AND [Rare Tag] = 'True' )"
                            End If
                        End With
                    End If
                End If
     
                Dim dv As New DataView(element.dtble)
                dv.RowFilter = RowFilter
                dv.RowStateFilter = DataViewRowState.CurrentRows
                dv.ApplyDefaultSort = True
                element.cbx.DataSource = dv
                element.cbx.DisplayMember = "Names"
                If element.cbx.Tag Is Nothing Then : element.cbx.Tag = "" : End If
                If dv.Find(element.cbx.Tag.ToString) > -1 Then
                    element.cbx.Text = element.cbx.Tag.ToString
                Else
                    element.cbx.ResetText()
                    element.cbx.Tag = ""
                End If
                RowFilter = OriginalRowFilter
            Next
        End Sub
    Pas d'exception, ni warning
    Aucun clignotement des controles à l'execution
    controles mutuellement exclusifs operationnels
    reset des text operationnel

    = Topic résolu

    Credits to Pol63


    P.S. : si tu as d'autres suggestions n'hésite pas, là je suis content d'avoir appris comment construire des collections avec Linq

  12. #12
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    pour ton select case géant, j'ai l'impression que tu transformes sub en main, left en right etc...

    dans ce cas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if element.cbx.Name.Contains("Left") then 
        MutualExCbxName = element.cbx.Name.replace("Left","Right")
      elseif element.cbx.Name.Contains("Right") then 
        MutualExCbxName = element.cbx.Name.replace("Right","Left")
      elseif element.cbx.Name.Contains("Sub") then 
        MutualExCbxName = element.cbx.Name.replace("Sub","Main")
      elseif element.cbx.Name.Contains("Main") then 
        MutualExCbxName = element.cbx.Name.replace("Main","Sub")
      else
        Throw New ApplicationException("cas non prévu : " & element.cbx.Name)
    end if
    avec au passage un throw qui déclenche un erreur, qui permet ici d'être averti d'une chose non prévue, car des fois on ne pense pas à tout, où une partie du code évolue et pas une autre ...
    m'enfin des fois les cas else sont normaux, et dans ce cas ne pas mettre de throw
    ! : contains et replace sont case sensitive par défaut

    autre chose, tester des string, c'est pas très POO, si ton but est de lier 2 combobox entre eux, il y a d'autres possibilités plus orientées objet
    par exemple faire un usercontrol avec les 2 combobox, ce qui permet d'avoir un référence objet vers les 2
    ou encore faire une classe héritant de combobox avec une propriété ComboboxLié as Combobox, et en mode design la propriété affichera les autres combobox sur le form, et là aussi tu auras un pointeur
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  13. #13
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    Hmmm en fait une petite explication s'impose :

    Non il ne s'agit pas de transformer, mais de rendre le contenu de 2 listes déroulantes mutuellement exclusives (les 2 étant basées sur la même datatable, via dataview).
    Par exemple si je sélectionne une valeur dans la 1ére combo, cette valeur doit disparaitre de la liste déroulante de la 2nde combo, comme ca l'utilisateur ne pourra jamais selectionner 2 fois le même item. et vice versa.
    Et cette règle ne concerne que les combos dont le nom commence par Main/Sub ou Left/Right, pas les autres.

    Une autre explication aussi sur l'utilisation de cette fonction:
    J'appelle la fonction à l'init de ma Form, afin de construire les listes déroulantes de toutes mes combos (leur .Text = "").
    Je l'appelle aussi lorsque la valeur de 2 combos de ma form ("Métier" et "Age", non concernées par la fonction) change, auquel cas toutes mes comboboxs doivent se "reset" de façon à se conformer aux nouvelles valeurs de ces 2 combos. Dans ce cas également, si les items déjà sélectionnés dans les combos à reseter étaient déjà "conformes" avant l'appel de la fonction, cette dernière laissera leur valeur telle quelle (en reprenant l'ancienne valeur stockée dans le .Tag). Cela implique le "Select Case géant" qui teste si la 2e combo d'une paire mutuellement exclusive sera resetée ou non, si pas de reset, je vire la valeur de Combo2.Tag de la liste déroulante de Combo1, et cela suivant l'ordre que la boucle For Each reset les combos.

    Sinon niveau syntaxe, c'est exactement comment j'avais codé à l'origine, avec plein plein de If elseif... ce qui faisait qu'à chaque bouclage de For Each la fonction devait evaluer chacun des If Elseif jusqu'au dernier... Le cours de P.Lasserre conseille dans ce genre de cas d'utiliser un Select Case, et en effet la fonction sélectionne directement le bon Case...

    En revanche, ton idée de POO pourrait etre interessante ^^
    Là je débute depuis 2 semaines et mon prochain objectif c'est de faire des tooltips dynamiques sur items de mes listes deroulantes. La POO je verrai cela + tard je crois...

  14. #14
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    Bonjour

    Malgré que ce topic soit résolu, je reposte ici plutôt que d'ouvrir un nouveau topic puisque le probleme que j'ai now concerne LINQ et le remplissage de combobox :


    En bref j'ai voulu faire ça (rien à voir avec la fonction de remplissage ci-dessus) en LINQ :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Me.AreasFilter.DataSource = (From Zone As Object In Me.DataSet.Enemies.ZoneColumn _
                                 Where Zone Distinct Select Zone)
    En français je souhaite remplir la combobox Areas avec le champ Zone de la datatable Enemies, dont les valeurs peuvent etre utilisées plusieurs fois d'où le DISTINCT. Le champ Zone est une colonne de type zone à liste déroulante, liée par une relation à une autre table unidimensionnelle contenant tous les enregistrements de noms d'endroits possibles.

    Mais VB2010 m'affiche une erreur que je ne comprends pas :
    L'expression de type 'System.Data.DataColumn' ne peut pas être interrogée. Vérifiez que vous n'omettez pas une référence d'assembly et/ou une importation d'espace de noms pour le fournisseur LINQ.
    J'ai vérifié ma table Enemies sur un datagridview, elle s'affiche niquel... cela ne m'a pas l'air non plus d'une erreur d'Assembly/Référence puisque LINQ.dll est chargée (et même utilisée intensivement dans la fonction de ce topic) donc j'en déduis qu'il s'agit d'une erreur de syntaxe ?

    EDIT: j'ai essayé une autre syntaxe en me basant sur l'exemple MSDN pour Distinct()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Me.AreasFilter.DataSource = DirectCast(Me.DataSet.Enemies.ZoneColumn, IEnumerable(Of String)).Distinct()
    Pas d'erreurs au design, mais Exception levée à l'execution pour DirectCast qui a foiré la conversion...

  15. #15
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    dans les 2 cas c'est la meme erreur

    l'instance ZoneColumn n'est pas une collections, donc tu ne peux pas l'utiliser pour in et tu ne peux pas la caster sur un ienumerable
    cette instance fournit des informations sur la colonne (nom, type etc...) mais ne contient pas les valeurs de la colonne

    il y a datatable.rows qui est une collection de ligne (mais pas les valeurs là non plus) et sur chaque ligne il doit y avoir une propriétés permettant d'avoir la valeur pour une colonne donnée

    peut etre un from r in rows distinct select r.valeurcolonne(nom_colonne)
    (je ne connais pas les propriétés des datatables mais faut trouver un truc dans le genre)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  16. #16
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    La syntaxe la + "proche" de ce que tu propose que j'ai pu trouver sans que VB me fasse ch... est:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Me.AreasFilter.DataSource = (From ZoneRow As Object In Me.DataSet.Enemies.Rows() Distinct Select DirectCast(ZoneRow, DataRow).Item("Zone"))
    La description de la propriété .Item("Column Name") dit bien qu'elle retourne les valeurs stockées dans la colonne ayant le nom spécifié.

    A l'execution, ArgumentException
    DataBinding complexe accepte IList ou IListSource comme source de données.

  17. #17
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    linq retourne un IEnumerable
    datasource veut un IList ou un IListSource ; c'est comme mettre monbouton.text = monlabel, .text attend un string, pas un label

    rajoute .ToList après la parenthèse de fin List implémente IList
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  18. #18
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    140
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 140
    Points : 37
    Points
    37
    Par défaut
    D'accooooord je comprends mieux, en effet ca marche avec .ToList mais il reste un hic: la syntaxe précédente m'affiche plein de doublons. Je crois que le Distinct ne s'applique qu'aux DataRows, or je voulait qu'il ne s'applique que sur les valeurs de la colonne Zone.

    EDIT: j'ai trouvé la solution
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Me.AreasFilter.DataSource = (From ZoneRow As Object In Me.DataSet.Enemies.Rows() Select DirectCast(ZoneRow, DataRow).Item("Zone")).Distinct.ToList
    Merci Pol63

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 06/03/2010, 18h07
  2. je cherche fonction de remplissage (pot de peinture)
    Par Faudel24000 dans le forum 2D
    Réponses: 12
    Dernier message: 11/08/2009, 00h07
  3. [AC-2007] Remplissage d'un contrôle en fonction du remplissage d'un autre
    Par Razorback dans le forum IHM
    Réponses: 7
    Dernier message: 19/05/2009, 09h29
  4. [Java2D]Fonction de remplissage
    Par Guybrush dans le forum 2D
    Réponses: 6
    Dernier message: 14/05/2007, 11h23
  5. Réponses: 2
    Dernier message: 19/12/2006, 12h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo