1 combobox et 1 listbox pour remplir un formulaire
bonjour , n'ayant jamais appris la programmation visual basic, je galère sur des choses que beaucoup vont trouver simples,mais après une semaine de recherche, j'abandonne et j’espère trouver les réponses a mes problèmes sur ce forum,merci d'avance a ceux qui voudront bien m’éclairer.
j'essaye de créer un formulaire en vba dans lequel j'ai une combobox dans laquelle s'affiche des noms de machine qui se trouvent dans la colonne A de la bdd, une listbox qui correspond a un code d'objet de la colonne B, quand je choisis une machine dans la combobox,et un code qui s'affiche dans la listbox,les données correspondant a la ligne machine + code doit remplir des textbox et des checkbox,sachant que la machine peut apparaitre plusieurs fois dans la colonne A ainsi que le code peut être plusieurs fois dans la colonne B mais le couple machine + code n'existe que sur une seul ligne.Sur cette partie mon problème c'est que quand je choisis une machine et un code ,les valeurs dans les textbox et les checkbox restent les mêmes quelque soit la machine choisi pour un même code.
2eme problème,comment jumeler une fonction modification avec celle pour enregistrer une nouvelle ligne si la ligne machine + code existe
et enfin dernier problème ,peut on afficher les éléments de la listbox par ordre alpha
je sais, ça fais beaucoup de problèmes a régler ,je joint le fichier au cas ou
merci
Bonsoir,
Pas de plage nommée, pas de boucle (ou imbrication inadéquate...), pas de tableau dans le code, et on voit mal comment on va pouvoir suivre les évolutions (une base est toujours amenée à évoluer)...
Un point positif, il n'y a qu'un seul Select !
Et il y a grandement matière à opérer pour améliorer tout ça, si bien entendu tu en es d'accord !
le couple machine + code n'existe que sur une seul ligne
Je veux bien le croire mais au premier essai, je crée un doublon !
Quelque chose ne va donc pas !
On s'attend en outre à pouvoir modifier un enregistrement ou bien en créer un nouveau, mais cette distinction n'existe pas, et on crée systématiquement un nouveau.
La conception (et les objectifs du projet) me paraissent donc d'abord à clarifier.
Cordialement.
Merci pour l'intérêt que tu portes à mon (mes
Ok ! Il faut donc commencer par élucider la problématique de conception ajout/modification car c'est la conception et les objectifs du projet qui vont conditionner l'organisation d'ensemble.
On a actuellement en place un dispositif qui ajoute exclusivement, y compris des doublons non prévus... Que doit-il en être ? C'est essentiel pour gérer le système et mettre en place le dispositif qui convient aux objectifs...
Cordialement.
De nouveaux codes vont s ajouter par la suite à la colonne B d'où ma textbox5 qui me servira à taper les nouveaux codes et les à ajouter à tel ou tel machine en créant une nouvelle avec le bouton enregistrer
D'accord que en ajoutant un code tu crées un nouvel enregistrement... Mais un enregistrement existant ne subit-il jamais de modification une fois créé ?
Lorsque tu choisis une machine, tu cliques sur un code dans la ListBox, tu affiches une enregistrement existant, qu'il n'est pas prévu que tu modifies (aucun dispositif pour cela). Donc si tu cliques alors sur enregistrer, tu crées un doublon, là totalement identique à celui existant déjà !
Si tu ne dois pas pouvoir modifier l'enregistrement doit être bloqué dans le cas où il génèrerait un doublon...
J'ai du mal à voir l'objet de la chose !
Si tu n'es pas plus loquace sur ce que tu veux obtenir, je ne sais pas ce que je pourrais faire ! Travailler sur le code est une chose mais cela n'a de signification que pour réaliser un objectif...
En fait quand je clic sur une machine (combobox1) ,la listbox doit afficher tous les codes qui correspondent à cette machine,ensuite je choisis un de ces codes et la toutes les valeurs de la ligne combobox1 + valeur choisit dans la listbox s'affichent dans les textbox 1 à 4 et dans les checkbox ,ces 8 box doivent pouvoir être modifiees et réenregistrees dans cette ligne existante
Si le code n'existe pas pour une machine ,je dois pouvoir ecrire ce code dans la textbox 5 et remplir les 8 box avec des nouvelles valeurs et de pouvoir enregistrer cette nouvelle ligne combobox1 + nouveau code sur la première ligne vide.
J'espère que c'est plus clair
Désolé pour les temps de réponse,je suis au boulot et merci pour ta patience,
Cordialement
Bonjour,
C'est noté ! Il y a donc bien à distinguer une fonction modification et une fonction création. Ce qui identifie un enregistrement, c'est le couple machine-code : couple existant, c'est une modif ; nouveau couple, c'est une création...
Pour compléter le panorama, y a-t-il lieu de prévoir la possibilité de supprimer un enregistrement ?
Et d'autre part de pouvoir insérer une nouvelle machine ?
Cordialement.
bonjour et merci pour ta reactivité
cordialement
Bonjour,
Pour te permettre de suivre exactement les modifications et méthodes utilisées, je vais fournir des indications au fur et à mesure, ce qui m'évitera d'en oublier à la fin, et te permet de les engranger progressivement.
La première modif. que j'opère est nommer une plage dynamique pour accéder directement à la base.
Le caractère dynamique (c'est à dire que lorsque l'on étend ou rétrécit la plage, le nom s'adapte toujours exactement à son état) et fourni par la définition au moyen d'un formule :
On va nommer la 1re colonne de ta base, au lieu de la définir par : A2:A9, on la situe par rapport à A2 (cellule supérieure gauche, fixe).
On utilise la fonction DECALER : =DECALER(Feuil1!$A$2;;;nbLignes;nbCol). On ne décale pas A2, d'où les 2e et 3e arguments sont laissés à 0 (décalages ligne et colonne), mais les 3e et 4e permettent de définir le nombre de lignes et le nombre de colonnes (c'est ce qui fournit le caractère dynamique).
On définit donc le nb de lignes par le nombre de valeur de la colonne A, diminué de 1 correspondant à l'en-tête : NBVAL(A:A)-1
Comme on ne définit qu'une colonne, on n'a pas à définir le nombre de colonne.
Tu trouveras donc dans le gestionnaire de noms, comme référence du nom (BMach) que j'ai donné à la colonne Machines de ta base, la formule suivante :
=DECALER(Feuil1!$A$2;;;NBVAL(Feuil1!$A:$A)-1)L'avantage d'un nom (et qui plus est d'un nom qui sera toujours valide) est que l'on atteint directement la base par le nom, sans avoir à calculer l'adressage à partir de la feuille. On adresse à partir de la plage nommée : A2 est la cellule (1, 1) de la plage, et on peut adresser n'importe quelle cellule à partir de là. Tu veux atteindre la cellule de la 3e ligne et dernière colonne de la base, ce sera la cellule (3, 10)...
Le fait de ne nommer qu'une colonne n'empêche nullement d'atteindre les autres (on est en adressage relatif). Ne nommer qu'une seule cellule suffirait d'ailleurs pour tout adresser à partir de cette cellule. Mais nommer la colonne permet de disposer à tout moment du dimensionnement en lignes de la base : [BMach].Rows.Count fournit le nombre de lignes sans avoir à le rechercher.
On pourrait nommer toute la base (en dimensionnant le nombre de colonne). Mais se limiter à une colonne offre une plus grande souplesse dans certains cas...
Tu as besoin de cibler la base entière : [BMach].Resize(, 10) le fait (extension de la plage nommée à 10 colonnes)
Tu as besoin de cibler la 3e colonne : [BMach].Offset(, 2) le fait en décalant la plage nommée de 2 colonnes...
Ce type de manip. pourrait aussi se faire avec un nom étendu à la base entière, mais avec des expressions plus longues.
Il faut comprendre le principe et choisir la solution minimale pour laquelle on peut anticiper le plus d'économies pour la suite.
(je scinde pour limiter à des pavés pas trop long et homogènes)
merci pour toutes ces explications ,j'ai nommé la plage dynamique comme demandé
Noté pour pas de suppression et pas d'ajout de machine...
Dans la foulée : bouton pour lancer le Userform (sur la feuille base). J'ai intitulé "Réglages" mais tu pourras choisir l'intitulé qui te convient le mieux, de même que tu peux modifier à ta convenance le nom de la feuille (puisqu'il ne sera pas utilisé dans le code).
Phase suivante, on va commencer à s'occuper du Userform : renommer les contrôles.
Plusieurs raisons à cela :
- Pas la première au niveau logique, mais qui n'est pas sans importance pour le rédacteur du code, pour les raccourcir (moins de temps pour le taper et moins de risque d'erreur de frappe...)
- Pour que leur utilisation soit plus explicite (compromis à faire avec la longueur, il faut surtout que celui qui intervient dessus puisse s'y retrouver facilement) : on a coutume d'utiliser un préfixe indiquant le type de contrôle, suivi d'un élément évocateur de son rôle).
cbEnr et cbQ permettront de distinguer plus aisément que CommandButton1 et 2 les bouton d'Enregistrement et Quitter.
Au cas particulier de ton Userform, je double les CheckBox de réglages 5 à 8 par des TextBox qui afficheront oui ou non selon le choix.
Je déroge à la règle de préfixage pour ne pas préfixer la ComboBox et les TextBox qui contiendront les valeurs à affecter éventuellement à la base. Je renomme donc tout cet ensemble avec R (pour réglage) suivi d'un numéro d'ordre : R1 à R10, le numéro d'ordre correspondant au numéro de colonne dans la base.
Je pourrai ainsi utiliser des boucles, tant pour récupérer des valeurs dans la base à affecter aux contrôles que pour récupérer les valeurs des contrôles à affecter à la base (en ayant une correspondance colonne-n° d'ordre du contrôle.
Je renomme la ListBox : lbxR2 (en correspondance avec la TextBox R2 (objet) et les CheckBox : chbR7 à chbR10 (en correspondance avec les TextBox R7 à R10).
J'ajoute encore une étiquette en regard de la TextBox objet, qui me permettra d'afficher : Modification (lorsque tu as cliqué dans la ListBox pour produire l'affichage des valeurs d'un enregistrement) ou NOUVEAU (si tu modifies la valeur de la TextBox par une valeur non listée dans la ListBox). Cela informe l'utilisateur du mode dans lequel il se met... (on verra plus tard la façon dont le code va le déterminer).
Pour terminer cette phase, la procédure d'initialisation (à l'ouverture du Userform) qui n'a pour fonction que d'alimenter la ComboBox avec les noms de machines :
Private Sub UserForm_Initialize()
Dim d As Object, mach, i%, j%, m$
Set d = CreateObject("Scripting.Dictionary")
With [BMach]
For i = 1 To .Rows.Count
d(.Cells(i, 1).Value) = ""
Next i
End With
mach = d.keys
For i = 0 To UBound(mach) - 1
For j = i + 1 To UBound(mach)
If mach(j) < mach(i) Then
m = mach(j): mach(j) = mach(i): mach(i) = m
End If
Next j
Next i
R1.List = mach
End SubCela va nous permettre de rappeler quelques règles essentielles d'écriture du code (qu'il conviendrait de ne jamais perdre de vue).
D'abord l'indentation ! Que le code soit indenté dans les règles ou qu'il soit écrit n'importe comment, pour VBA aucune différence, tant qu'il peut exécuter, il exécute, quand il ne peut plus, il bloque ! Et c'est notamment là que l'indentation montre une de ses raisons d'être, quand il s'agit de savoir pourquoi ça bloque !
Lorsqu'on écrit une procédure, on tape la déclaration de procédure : Sub NomDeLaProc (arguments éventuels avec leur type), VBA complète avec End Sub et nous laisse une ligne pour commencer à écrire le contenu de la proc.
Sub et End Sub se trouvent alignés à la marge du Module. Indenter consiste à se mettre en retrait d'une tabulation à partir de cette marge pour taper le code. Hormis la déclaration de procédure ne sont alignés sur la marge que les étiquettes de branchements (notamment en cas de gestion d'erreur) si l'on en met : cela peremet de les voir immédiatement au premier coup d'oeil.
Variante optionnelle acceptable : certains mettent à la marge les déclarations de variables. (Je trouve que cela alourdit, mais c'est une option acceptable, les déclarations de variables se faisant en tête de procédure).
L'alignement de base du code écrit se trouve donc en retrait d'une tabulation par rapport à la marge. Ensuite à chaque fois que l'on rencontre un bloc d'instruction (With... End With, If... Else... End If, For... Next..., Do... Loop, etc.) le code intérieur à ce bloc est mis en retrait d'une tabulation. Il peut y a voir ainsi plusieurs tabulations successives lorsque des blocs sont imbriqués...
Lorsqu'on regarde un code indenté, lorsqu'on trouve un With on aura un End With sur le même alignement vertical, et rien entre les deux sur cet alignement. Un For sera aligné avec son Next. Un If avec son End If et on ne pourra trouver sur l'alignement que des ElseIf éventuels et/ou un Else...
Cela permet une vue structurelle immédiate de la procédure, la détection visuelle de pas mal d'erreurs possibles, une lecture au moins 4 fois plus rapide de la procédure, en sachant ce que l'on lit...
Autre rappel : on distingue le code exécutable du code non exécutable (constituée par les déclarations). Le code exécutable est obligatoirement inclus dans les procédures. Les déclarations peuvent être hors procédures, au niveau Module, le cas échéant.
Les déclarations à l'intérieur de la procédure sont toujours placées en tête (cela permet de les trouver sans chercher, de vérifier qu'on déclare bien toutes ses variables, et à VBA d'être plus efficace...)
Venons-en à la proc. d'initialisation, pour illustrer et éventuellement l'expliquer. La première ligne après déclaration de proc. déclare les variables. Rappel qu'il est toujours recommandé de typer ses variables, et que toute variable doit être typée individuellement (on ne peut affecter un type collectivement). Si on ne type pas la variable est automatiquement de type Variant (inutile de déclarer ce type donc !)
On déclare donc ici une variable objet, une variable non typée (donc de type Variant, qui sera utilisée pour accueillir un tableau, 2 variables Integer et une variable String. Les caractères % et $ affublés aux noms de variables sont ce que l'on nomme caractères de déclarations de types : % signfiant As Integer et $ signifiant As String (il en existe encore quelques autres...) Cela permet de raccourcir notablement l'écriture de déclarations.
On a à définir la liste à affecter à la ComboBox, cette liste est constituée par la liste des machines en col. A de la base, sans doublons.
On utilise pour créer cette liste sans doublons l'objet Dictionary. Un dictionnaire est constitué d'éléments comportant une clé (qui identifie l'élément) et une valeur (ou Item). On parcourt donc la colonne A (voir la méthode, utilisant la plage nommée directement) et à chaque ligne on crée un élément de dictionnaire dont la clé est constituée par la valeur de la cellule, soit le nom de la machine (on n'affecte qu'une valeur nulle car on ne s'intéresse qu'aux clés). Un dictionnaire ne pouvant comporter deux clés identiques, cette méthode élimine naturellement tout doublon, et on se trouve à la fin en présence d'un dictionnaire comportant autant d'élément qu'il y a de machines uniques dans la base.
On constitue un tableau de ces clés (affectation à la variable prévue pour cela).
Et pour parfaire, on va trier le tableau de façon à mettre les machines dans l'ordre au cas où elles ne le seraient pas.
Retenir la méthode classique de tri d'un tableau : double boucle dans laquelle on compare chaque élément à chacun des autres, partant du début du tableau, on compare le premier aux suivants s'il est plus petit (on trie dans l'ordre ascendant) on ne bouge pas, si le second est plus petit, on opére une substitution (on utilise une variable externe pour switcher les deux éléments). A la fin du premier tour de la boucle externe on aura placé le plus petit élément en tête, au tour suivant on aura placé le plus petit élément restant en 2e position, et ainsi de suite.
Noter que la ligne opérant la substitution comporte des : (deux-points). Ce caractère est un séparateur de lignes de code, il permet d'écrire sur la même ligne physique plusieurs lignes de code. Il y a donc sur cette ligne en fait 3 lignes de code.
Ceci fait, on affecte le tableau à la Combo en utilisant la méthode List (affectation globale, plus économique que d'affecter élément par élément au moyen de la méthode AddItem, et on aura pris le temps de les trier...)
Bonne journée.
NB- Je te fournirai le fichier à la fin...
j'ai remplacé mes checkbox par des textbox et j'ai renommé en R1 a R10 en suivant les colonnes,et j'ai entré la premiere partie du programme
C'est instructif de s'entraîner
On poursuit pas à pas ?
Le maillon suivant est la sélection dans la Combo d'une machine...
On est obligé d'anticiper un peu sur ce qui pourra se passer par la suite ! Car en effet à tout moment l'utilisateur peut intervenir sur ce contrôle, il peut changer de machine alors que tout est rempli, et il peut même effacer la valeur.
On a donc à prévoir que dans le cas où il change, il faut effacer le reste (qui reviendra au fil des manipulations qu'il fera après avoir choisi la machine, et s'il ne choisit rien, il faudra avoir effacé... On met donc en place une proc. auxiliaire d'effacement.
Sub Réinit(Optional tt As Boolean)
Dim i%
If tt Then
With lbxR2
.ListIndex = -1
.Clear
End With
End If
For i = 2 To 10
Controls("R" & i).Value = ""
Next i
lbNM.Visible = False
lgn = 0
End SubOn la dote d'un argument optionnel, après avoir encore anticipé sur la suite... la même question va se poser de façon similaire avec les choix dans la ListBox. Dans le cas de la ListBox, on aura les mêmes éléments à effacer, sauf la liste de la ListBox ! (puisque là on n'aura pas changé de machine).
Donc si on lance la procédure avec True, on efface tout : les TextBox (boucle d'effacement) et la liste de la ListBox, on rend invisible l'étiquette de signalisation (Nouveau/Modif), et on remet à 0 une variable Integer (déclarée au niveau Module) et chargée de conserver le numéro de ligne de l'enregistrement affiché (j'y reviendrai).
Si on la lance sans argument (il est optionnel) ou False, elle fera la même chose mais en laissant intacte la ListBox.
On peut donc passer au choix dans la Combo.
Private Sub R1_Change()
Dim i%, mach$
Réinit True
If R1.ListIndex > -1 Then
mach = R1.Value
With [BMach]
For i = 1 To .Rows.Count
If .Cells(i, 1) = mach Then
lbxR2.AddItem .Cells(i, 2)
End If
Next i
End With
End If
End SubDans tous les cas, on aura à effacer et tout, on commence par ça. Ensuite on n'a à intervenir que si une machine a été choisie, donc si ListIndex est supérieur à -1. Dans ce cas, on prélève le nom de la machine dans une variable, on parcourt la colonne A de la base, et chaque fois qu'on trouve la machine, on ajoute le code correspondant à la liste de la ListBox.
Je ne m'étends pas sur le principe, la méthode est semblable à celle que tu utilisais, et en termes de procédure rien de nouveau...
Mais un point mérite d'être explicitée : on utilise AddItem, à la fois parce que le nombre d'item est réduit et pourra être limité à un (et dans ce cas List n'est pas utilisable, il faut au moins 2 éléments... [A retenir]) et que dans ces conditions un tri serait superflu...
Tu notes aussi qu'une boucle simple suffit amplement. A cet égard, si tu as du temps et la curiosité... je t'invite à tester ton imbrication de boucles (dans ta procédure précédente) en suivant manuellement pas à pas les valeurs successives prises par i et se qui se passe, pour mesurer par quels détours tu arrives au résultat...
Il est l'heure de s'occuper du repas avant de passer à la suite...
j'ai omis de vous dire que j'ai voulu simplifier le fichier en pensant que ça n'aurait pas d'incidence sur le programme ,en mettant des noms de machine comme Mach1,2,3,4,5 alors que en realité ces noms de machines sont mf1,mf2,mg1,mg2,mj1 et que les valeurs de reglages comme r1m1gn23 sont en realité des chiffres positifs (125 par ex) ou négatifs (-256 par ex),pour les codes colonne B c'est bon, desolé pour ce manque de clarté tardif,
je vous rejoint le fichier avec les bonnes valeurs
Je me doutais bien que les désignations de machines étaient ainsi pour l'exemple... Cela n'a aucune implication, les données étant de même type. Les indications numériques pourraient éventuellement en avoir si tu procédais à des opérations numériques, ce qui n'est pas le cas.
Au passage mémorise que lorsque tu mets des valeurs numériques dans des TextBox ou ComboBox, une fois qu'elles y sont elles deviennent par définition des valeurs texte. Pour opérer des opérations numériques avec des valeurs issues de TextBox dans le code, il faut donc les convertir. Mais lors de l'affectation d'une valeur de TextBox à une cellule Excel, ce dernier reconnaît son caractère numérique et le privilégie, tu retrouves donc un nombre dans la cellule.
(NB- des problèmes d'affectation ne se posent en principe que pour les dates, mais pour d'autres raisons, il faut alors les convertir pour forcer la reconnaissance en tant que dates selon les paramètres régionaux du système, et éviter ainsi une inversion éventuelle mois-jour provenant du format américain).
L'étape suivante, la ListBox étant alimentée, concerne le choix dans cette ListBox :
Private Sub lbxR2_Click()
Dim mach$, obj$, i%, j%
Réinit
If lbxR2.ListIndex > -1 Then
mach = R1.Value: obj = lbxR2.Value
With [BMach]
For i = 1 To .Rows.Count
If .Cells(i, 1) = mach And .Cells(i, 2) = obj Then
For j = 2 To 10
Controls("R" & j).Value = .Cells(i, j)
Next j
AffichNouv False
lgn = i
Exit For
End If
Next i
End With
End If
End SubComme avec la Combo, on efface (sauf la liste de la ListBox) avant de réintroduire les informations issues du choix.
De la même façon, on parcourt la base, mais cette fois on teste la correspondance simultanée de la colonne A avec la valeur de la Combo (machine) et de la colonne B avec la valeur de la ListBox (objet). On a recueilli ces deux valeurs dans des variables (NB- dès lors que l'on doit utiliser la valeur d'un élément de façon répétitive, on a intérêt à affecter ces valeurs à des variables, VBA est plus rapide avec des variables...)
Lorsque l'on trouve la correspondance cherchée, une boucle permet d'effectuer l'affectation des valeurs de la ligne aux TextBox, puis on peut sortir de la boucle de parcours de la base puisque le couple machine-objet est réputé unique dans la base.
On fait tout de même deux petites opérations encore :
Lancement d'un procédure auxiliaire : AffichNouv avec le paramètre False.
Sub AffichNouv(nv As Boolean)
With lbNM
If nv Then
.Caption = "NOUVEAU"
.ForeColor = vbRed
Else
.Caption = "Modification"
.ForeColor = vbBlue
End If
.Visible = True
End With
End SubProcédure plutôt simple, on le voit : elle affiche une étiquette avec la mention NOUVEAU ou Modification selon qu'on lui a dit True ou False, et met le texte en rouge ou en bleu selon le cas. Tu pourras toujours modifier ou sophistiquer la mise en forme...
Une petite remarque utile que j'aurais pu faire tout à l'heure. Les procédures d'un module de Userform sont pour l'essentiel des évènementielles, destinées à réagir aux évènements affectant les contrôles. Elles sont donc automatiquement déclarées par VBA avec le mot clé Private (ce qui n'est pas un choix mais plutôt une information, car on ne dispose pas de la faculté de les rendre publiques...)
Je conseille toujours d'ailleurs de ne pas écrire ces déclarations mais d'utiliser les 2 listes déroulantes du module pour les faire insérer par VBA, cela évite parfois des surprises... et de les laisser là où VBA les positionne (sauf cas particulier), l'expérience montre qu'on les retrouve ainsi plus rapidement.
Pour ce qui est des procédures hors évènements, que l'on ajoute, on pourrait aussi les doter du mot clé Private, parce qu'elles n'ont pas de raison d'être appelée de l'extérieur du module. En ne le mettant pas, la procédure est publique (les procédures sont publiques par défaut, à l'inverse des variables qui sont privées par défaut). Elle n'a cependant aucune chance d'être appelée par inadvertance de l'extérieur, car elle est dans un module qui est, lui, privé. Ce pourquoi, je ne les déclare pas Private, dans la mesure où cela les distingue des procédures d'évènements et permet de les repérer plus facilement.
Il reste très souhaitable par contre de respecter les règles de répartition des différents éléments d'un module et de les placer dans la zone (General) en tête du module, à la suite des déclarations de niveau module s'il y en a.
Et dernière petite opération : on affecte à la variable lgn déclarée au niveau module le numéro de ligne de l'enregistrement que l'on a affiché.
Restait un petit point à voir dans la foulée, mais à suivre car ce post est déjà assez long !
La procédure ListBox a alimenté toutes les TextBox. On aura donc les mentions oui ou non pour les réglages 5 à 8. Mais il convient que cela se répercute aussi sur les CheckBox destinés à opérer manuellement sur ces réglages. On a donc mis en place une nouvelle procédure auxiliaire :
Sub RéglageOuiNon(coche As Boolean, n As Integer)
If coche Then
Controls("R" & n).Value = IIf(Controls("chbR" & n).Value, "oui", "non")
Else
Controls("chbR" & n).Value = IIf(Controls("R" & n).Value = "oui", True, False)
End If
End SubCette procédure est lancée par les interventions, soit sur les CheckBox :
Private Sub chbR7_Click()
RéglageOuiNon True, 7
End Sub(et même chose pour les 3 autres)
Si on coche ou décoche une CheckBox, la procédure est lancée avec True (indiquant qu'on a agi sur une CheckBox) et le numéro d'ordre du contrôle.
Dans ce cas elle affecte à la TextBox ayant le même numéro d'ordre la valeur oui ou non selon que l'on a coché ou décoché.
Soit en agissant sur les TextBox :
Private Sub R7_Change()
RéglageOuiNon False, 7
End Sub(et même chose pour les 3 autres)
Lorsque la valeur change, ce qui est le cas lors de l'exécution de la procédure ListBox, la procédure sera lancée avec False et le numéro d'ordre du contrôle.
Dans ce cas, selon la valeur oui ou non, la CheckBox correspondante sera cochée ou décochée.
Le fait qu'un changement dans une catégorie produit un changement dans l'autre a un effet en retour, qu'il est bon de ne pas méconnaître. On peut donc prévoir que la procédure sera relancée une seconde fois par le contrôle modifié par sa première exécution, cependant cela s'arrêtera là puisqu'ils se trouveront alors en adéquation...
Autre problème potentiel : la procédure Change des TextBox se déclenche à chaque frappe (ou effacement) de caractère lorsqu'on saisit manuellement, ce qui pourrait entraîner des effets pervers... mais l'affectation par une procédure affecte oui ou non directement d'une part, et d'autre part ces textbox ne sont pas utilisés manuellement (l'utilisateur dispose des CheckBox pour intervenir). Ils sont d'ailleurs verrouillés (propriété Locked mise à True, réglage à faire dans fenêtre de propriétés), et l'utilisateur ne peut les modifier manuellement.
Plus que deux étapes ! je souffle un peu.
Avant dernière étape : l'intervention sur la TextBox objet qui permet à l'utilisateur de générer éventuellement un nouvel enregistrement.
Private Sub R2_AfterUpdate()
Dim obj$, i%
obj = R2.Value
With lbxR2
For i = 0 To .ListCount - 1
If .List(i) = obj Then
.ListIndex = i
Exit Sub
End If
Next i
End With
AffichNouv True
lgn = 0
End SubOn vérifie que la valeur inscrite dans la TextBox ne figure pas dans la liste de la ListBox. Si elle figure déjà, elle opère la sélection de l'élément dans la ListBox. La procédure ListBox fera le reste.
Si non trouvé, la proc. se poursuit en provoquant l'affichage NOUVEAU et réinitialisant lgn à 0.
Dans tous cas l'utilisateur a la faculté de saisir ou modifier les différents réglages.
On arrive ainsi à l'enregistrement. Clic sur le bouton.
Private Sub cbEnr_Click()
Dim vrg(), ctl$, i%, r%
If R1.ListIndex = -1 Then
MsgBox "Aucune machine sélectionnée.", vbInformation, "Erreur"
Exit Sub
End If
For i = 2 To 10
If Controls("R" & i).Value = "" Then
ctl = IIf(i < 7, "lb", "chbR") & i
ReDim Preserve vrg(r)
vrg(r) = Controls(ctl).Caption: r = r + 1
End If
Next i
If r > 0 Then
MsgBox "Toutes les rubriques ne sont pas saisies." & Chr(10) & "Rubriques manquantes : " _
& Join(vrg, ", ") & ".", vbInformation, "Saisie incomplète"
Exit Sub
End If
ReDim vrg(9)
For i = 1 To 10
vrg(i - 1) = Controls("R" & i).Value
Next i
With [BMach]
If lgn = 0 Then
lgn = .Rows.Count + 1
ctl = "ajouté."
Else
ctl = "modifié."
End If
.Cells(lgn, 1).Resize(, 10).Value = vrg
End With
MsgBox "L'enregistrement a été " & ctl, vbInformation, "Enregistrement."
R1.ListIndex = -1
End SubTout ce qui précède la ligne Redim vrg(9) constitue des vérifications que toutes les valeurs soient servies.
D'abord qu'une machine soit sélectionnée, sinon message et interruption. (NB- On teste la valeur de ListIndex pour être sûr que la machine figure dans la liste)
Ensuite, on défile les autres contrôles en boucle pour s'assurer qu'ils contiennent une valeur. Si une valeur manque : on utilise temporairement le tableau vrg pour recueillir l'indication de la valeur manquante en incrémentant ce tableau au fur et à mesure (en incrémentant une variable r pour redimensionner le tableau en conservant les valeurs antérieurement affectées).
Pour l'identification, on utilise une variable String à laquelle on affecte le nom du contrôle dont on prélèvera le libellé (propriété Caption). Pour les 4 derniers, il s'agit des libellés des CheckBox, pour les précédents des libellés des étiquettes (Labels), d'où la nécessité de les identifier selon le point où l'on en est de la boucle.
Si des manques sont constatés, message avec indication des manquants (noter qu'on les liste en transformant le tableau constitué précédement en texte en utilisant la fonction Join qui liste les éléments en les séparant par une virgule suivie d'une espace).
Si tout est OK, on peut alors poursuivre en redimensionnant le tableau vrg à 9 (10 éléments de 0 à 9) pour recueillir les valeurs des réglages, par une boucle sur les 10 contrôles...
On passe à l'affectation : on teste la variable lgn, si elle est à 0, c'est un enregistrement nouveau, on lui affecte donc le nombre de lignes de la plage nommée +1 pour cibler la ligne d'inscription, on affecte "ajouté" à une variable texte pour compléter le message qui confirmera le bon déroulement de l'opération ; si lgn a déjà une valeur, c'est la ligne d'inscription (à modifier), on n'y touche pas et on affecte "modifié" à la variable texte sus-indiquée.
On affecte enfin directement toute la ligne en affectant le tableau à la plage dimensionnée sur 10 colonnes...
Un message pour confirmer l'opération à l'utilisateur.
Et on réinitialise le ListIndex de la Combo à -1, ce qui a pour effet de réinitialiser le Userform en effaçant tout le reste.
Il ne te reste plus qu'à tester en situation, et vérifier si on a bien pensé à tout...
Cordialement.
c'est génial d’être tombé sur quelqu'un d'aussi expérimenté, j'avoue que je relie plusieurs fois pour comprendre car pour un novice c'est assez dur mais j'avance grâce a vous,mille merci et le fichier que vous avez créé correspond exactement voir même dépasse mes attentes,ça marche du tonnerre, il n'y a plus qu'une tout petite chose ,est il possible d'avoir les codes de la listbox qui s'affichent en ordre alphabétique car au fur et a mesure de l'ajout de code,ça va devenir de plus en plus dur de repérer les codes dans la listbox,si ce n'est pas possible ,ce n'est pas grave car vous avez déjà fais beaucoup.
Pas de problème, mais pas tout de suite ! Je ne touche plus à rien à cette heure...