Résolu Excel VBA créer UserForm création et UserForm de Modification

  • Auteur de la discussion cassivellaunos
  • Date de début

cassivellaunos

Nouveau membre
Bonjour,

J'aurai besoin de la lumière de "ceux qui savent" pour réaliser un formulaire de saisie qui enverrai les données dans une liste.
Dans un premier temps j'ai réalisé (en partie merci internet !) ceci :
Code:
Sub DerniereLigneVide() 
' 
' Sub DerniereLigneVide Macro 
' Macro enregistrée le 28/07/2010 par Cassivellaunos 
' 
Dim Ligne As Integer 
Application.ScreenUpdating = False 
Range("A5:c5" ).Copy 
Sheets("Feuil2" ).Select 
Ligne = ActiveSheet.Range("A65536" ).End(xlUp).Row + 1 
Range("A" & Ligne).Activate 
ActiveSheet.Paste 
Sheets("Feuil1" ).Select 
Application.CutCopyMode = False 

End Sub
Ca marche mais la liste est créée dans le même classeur que les formulaires. Je voudrai (pour plus de souplesse) que la liste soit dans un autre classeur. En VBA j'ai compris que "ActiveSheet.Paste" colle dans le classeur actif dans la feuil2 le résultat de la saisie dans la feuil1. Par contre j'ai besoin de conserver (sous forme de feuilles) l'historique des saisies. Je suis donc limité à 256 feuilles (excel 2000).
J'ai réalisé cette autre macro - qui créee des feuilles avec les noms saisis dans une liste :
Code:
Sub NommerFeuilles() 
' 
' NommerFeuilles Macro 
' Macro enregistrée le 29/07/2010 par Cassivellaunos 
' 
'Selection de la 1er cellule de la colonne A 
Range("A1" ).Select 
Dim cpt As Integer 
cpt = 1 
Do 
DoEvents 
SendKeys "{Down}", True 
A = A + 1 
Sheets(A).Name = ActiveCell 
'Si A = 12, 
If A = 13 Then 
cpt = cpt - 1 
End If 
Loop Until cpt = 0 
On Error GoTo 1 
1 MsgBox "Il est impossible de nommer la feuille" & A & ". Vérifier que le contenu de la cellule n' est pas nul." 

End Sub
Comment pourrai-je intégrer ces 2 macros ?
Comment gérer plusieurs classeurs de formulaires (à cause de l'historisarion des données) ?

Je ne connais pas grand chose en VBA - mes macros sont perfectibles - si quelqu'un voudrai bien m'aider je l'en remercie par avance.

Cassivellaunos
 

zeb

Modérateur
Re-Salut ;)

Beurk, quel code horrible. C'est si laid que je vais t'aider à ne plus recommencer des choses aussi moches.

Tout d'abord, il est interdit d'utiliser le presse-papier dans une macro bien pensée. Imagine que plusieurs macros travaillent en même temps. Où que d'autres programmes se permettent ce genre de choses. Quelle pagaille !

Donc revoyons ce bout de code qui marche et dont tu étais peut-être fier.
Code:
Sub DerniereLigneVide()
'
' Sub DerniereLigneVide Macro
' Macro enregistrée le 28/07/2010 par Cassivellaunos
'
    Dim Ligne As Integer
    Application.ScreenUpdating = False
    Range("A5:c5" ).Copy
    Sheets("Feuil2" ).Select
    Ligne = ActiveSheet.Range("A65536" ).End(xlUp).Row + 1
    Range("A" & Ligne).Activate
    ActiveSheet.Paste
    Sheets("Feuil1" ).Select
    Application.CutCopyMode = False
End Sub

Les commentaires n'apportent rien. Soit on les vire, soit on en écrit des plus utiles.
Tu définies la variable Ligne. C'est TRES bien. :)
Tu retires la mise à jour en temps réel. Mouais. Ça, c'est un aveu d'impuissance. Attends un peu, ton programme n'en aura plus besoin. En passant, t'as oublié de remettre la valeur à Vrai.
Ligne 8, tu désignes une zone de cellules sans en préciser la feuille. Soit. Bon la copie par presse-papier c'est mal. On l'a déjà dit.
Ligne 9, tu changes de feuilles. C'est mal. Encore une fois, imaginons que deux macros tournent en même temps. On risque la pagaille !
Ligne 10, tu désignes une cellule en précisant la feuille. Ah bon. Ben pourquoi ?
Ligne 11, tu sélectionnes une cellule. C'est mal. Toujours un risque de pagaille .... :D
Ligne 12, bon, en espérant que personne n'ait changé la feuille ou la cellule active.

Eh, c'est facile de critiquer, non ?

D'abord, Voyons cette écriture :
Code:
Sheets("Feuil2" ).Select
Ligne = ActiveSheet.Range("A65536" ).End(xlUp).Row + 1
Tu sélectionnes un truc pour ensuite de servir de ce qui est actif. Raccourcissons ça :
Code:
Ligne = Sheets("Feuil2" ).Range("A65536" ).End(xlUp).Row + 1
N'est-ce pas plus simple ? En plus, c'est à l'abri de ce qui sélectionné ou pas. Plus de pagaille à craindre !
De la même façon :
Code:
' // Pas bien
Range("A" & Ligne).Activate
ActiveSheet.Paste
' // Bien
Range("A" & Ligne).Paste
' // Encore mieux
Sheets("Feuil2" ).Range("A" & Ligne).Paste
Qu'en penses-tu ?

Maintenant, relisons l'aide sur la méthode Copy de la ligne 8, telle qu'elle s'applique à un Range, donc :[citation][nom]Microsoft Visual Basic : Aide[/nom]Copy, méthode
Cette méthode copie la plage vers la plage spécifiée ou dans le Presse-papiers.[fixed]expression.Copy(Destination)[/fixed]expression Obligatoire. Expression qui renvoie un objet Range.

Destination Argument de type Variant facultatif. Spécifie la nouvelle plage dans laquelle la plage spécifiée sera copiée. Si vous ne spécifiez pas cet argument, Microsoft Excel copie la plage dans le Presse-papiers.
[/citation]
Ah, et si on mettait dans la ligne 8, en destination, la cellule définit en ligne 11 :
Code:
Dim Ligne As Integer
Ligne = Sheets("Feuil2" ).Range("A65536" ).End(xlUp).Row + 1
Sheets("Feuil1" ).Range("A5:c5" ).Copy Sheets("Feuil2" ).Range("A" & Ligne)
N'est-ce pas vachement plus simple ?

Par principe, j'utilise Worksheet plutôt que Sheet. C'est plus précis. Demande à l'aide d'Excel pourquoi ;)
Et il se trouve que Sheets("Feuil2" ).Range("A65536" ).End(xlUp) est une cellule dont on récupère la ligne pour en préciser une cellule qui est donc la même :pt1cable: ... :D

Ça donne :
Code:
Sub DerniereLigneVide()
    Worksheets("Feuil1" ).Range("A5:C5" ).Copy Worksheets("Feuil2" ).Range("A65536" ).End(xlUp)
End Sub

As-tu tout compris ?
Si oui, on passera à la suite ...

;)

 

cassivellaunos

Nouveau membre
Merci Zeb,
Je dis merci puisque j'ai lu toutes tes remarques jusqu'au bout alors qu'en m'en tenant juste au début - je n'aurai pas froid cet hiver... cà c'est sûr !
J'avais précisé que mes macros étaient perfectibles - heureusement !
Je ne maîtrise pas très bien la notion de temps réel et de copy cette dernière étant plus habituelle pour moi mais bien moins logique quand on sait paramétrer dans la logique.
En ce qui concerne : Worksheet(s) représente uniquement les feuille de calcul, alors que Sheet(s) représente n'importe quelle feuille (calcul, graphique...).
par contre je me demande pourquoi tu as utilisé "Worksheets en nommant la feuille1 et non Worksheet puisque dans notre cas il n'y en a qu'une ? Qui peut le plus peut le moins peut-être ?
On peut passer à la suite quand tu voudras et encore merci pour cette approche très Pro.
Cassivellaunos
 

zeb

Modérateur
Meilleure réponse
Salut :)

Digression sur Worksheets

Un classeur possède une liste de ses feuilles. Cette liste s'appelle Worksheets (avec un s, donc) et c'est au sens VB, une collection. Pour accéder à une de ces feuilles, on précise soit le nom soit le numéro de la feuille dans la méthode Item :
Code:
MsgBox "La feuille #1 s'appelle : " & ThisWorkbook.Worksheets.Item(1).Name
MsgBox "Le numéro de la feuille 'Feuil2' est : " & "ThisWorkbook.Worksheets.Item("Feuil2").Index

Bon, comme cela alourdit énormément l'écriture, on a inventé les méthodes par défaut. Quand on ne précise rien, pour une collection, la méthode par défaut est Item. En plus, contextuellement, on se passe de préciser le classeur. Ça donne :
Code:
MsgBox "La feuille #1 s'appelle : " & Worksheets(1).Name
MsgBox "Le numéro de la feuille 'Feuil2' est : " & "Worksheets("Feuil2").Index

Comme Worksheets est une collection, on peut itérer dessus, c'est à dire parcourir ces éléments, on peut connaître le nombre d'éléments, etc. En VB, l'itération dans une collection se fait avec un For Each :
Code:
Dim ws As Worksheet

MsgBox "Il y a " & Worksheets.Count & " feuille(s) dans ce classeur"
For Each ws In Worksheets
    MsgBox "La feuille n°" & ws.Index & " se nomme '" & ws.Name & "'."
Next

Il existe pleins d'autres collections, dont le nom est toujours au pluriel. Citons notamment Workbooks, la collection des classeurs ouverts dans Excel et Cells, la collection des cellules d'une feuille ou d'une zone de feuille.

Attardons-nous sur Cells.
Voici comment on désigne avec orthodoxie une cellule :
Code:
Workbooks.Item("classeur1").Worksheets.Items("Feuil1").Cells.Item(1, 1)
Mouais. :/
Voici la version raccourcie :
Code:
Workbooks("classeur1").Worksheets("Feuil1").Cells(1, 1)
. Si il n'y a pas de doute sur le classeur ou sur la feuille, on peut ne pas les préciser.

Range() renvoie une portion rectangulaire de feuille dont on précise deux coins opposés :
Code:
MsgBox Range(Cells(2, 3), Cells(4, 5)).Address
Pour accéder aux cellules de cette zone, on utilise Cells, bien sûr :
Code:
MsgBox Range(Cells(2, 3), Cells(4, 5)).Cells(2, 2).Value
Les deux premiers Cells sont relatifs à la feuille, le dernier, à la zone.
(Pour que l'exemple soit probant, mets quelque chose dans la cellule D3 ;) )

Par ailleurs, Range() accepte comme paramètre, soit une cellule, soit une adresse de cellule.
Code:
MsgBox Range(Cells(2, 3), Cells(4, 5)).Cells.Address
MsgBox Range("C2", Cells(4, 5)).Cells.Address
MsgBox Range(Cells(2, 3), "E4").Cells.Address
MsgBox Range("C2", "E4").Cells.Address
MsgBox Range("C2:E4").Cells.Address

Bon. un truc va nous simplifier la vie. Range() a Cells comme méthode par défaut. Donc on va pouvoir ne plus le préciser.
Code:
MsgBox Range("C2:E4").Address

Et comme une cellule, ça reste une zone de cellules rectangulaire quand même :
Code:
MsgBox Range("C2").Address

Voici donc l'adresse de la cellule C2 :
Code:
MsgBox Cells(2, 3).Address
MsgBox Range("C2").Cells.Address
MsgBox Range("C2").Cells(1, 1).Address
MsgBox Range("C2").Address
C'est quand même bien souvent plus pratique, non ?

Au fait, comme Cells est une collection, et que Range().Cells = Range(), Range() se comporte comme une collection :
Code:
Dim cellule

For Each cellule In Range("A1:B3")
    MsgBox cellule.Address
Next

Il se trouve que le type de données Cell n'existe pas en VBA/Excel. Curieux, non ? Ce qui existe, c'est "plage de cellules". Et ça se dit Range. (A ne pas confondre avec Range())

Donc la ligne 1 du code précédent s'écrit :
Code:
Dim cellule As Range

Quoi, t'es tout embrouillé ? :pt1cable:
T'avais qu'à pas me dire que tu lisais les messages jusqu'au bout :p

La suite

Parce que maintenant on passe à la suite.
Alors si je comprends bien, tu as une liste de noms de feuilles dans la colonne A, et tu voudrais renommer des feuilles. Facile.

Parcourons la colonne A.
Soit on en définit les bornes d'avance, soit on parcourt TOUTE la colonne, quitte à sortir en route.
Code:
Dim i As Long ' // Un entier

' // Version longue
For i = 1 To 65536
    ....
Next

' // Version "on réfléchit avant"
Dim ligne_der As Long
ligne_der = Range("A65536" ).End(xlUp).Row

For i = 1 To ligne_der
    ...
Next

' // Version "on verra en allant"
For i = 1 To 65536
    If Cells(i, 1).Value <> "" Then Exit For
    ....
Next

A ces versions où on utilise des nombres, je préfère celles-ci, où on utilise des cellules :
Code:
Dim c As Range ' // Une cellule

' // Version longue
For Each c In Rows(1)
    ....
Next

' // Version "on réfléchit avant"
Dim cellule_der As Range
cellule_der = Range("A65536" ).End(xlUp)

For Each c In Range ("A1", cellule_der)
    ...
Next

' // Version "on verra en allant"
For Each c In Rows(1)
    If c.Value <> "" Then Exit For
    ....
Next
J'apprécie particulièrement la dernière version.

Mais dans ton cas précis, on a besoin de gérer un compteur. Donc on va choisir une version avec entier :
Code:
For i = 2 To 65536
    If Cells(i, 1).Value <> "" Then Exit For
    MsgBox "Feuille n°" & i & vbCrLf _
           "Ancien nom : " & Worksheets(i).Name  & vbCrLf _
           "Nouveau nom : " & Cells(i, 1).Value
    Worksheets(i).Name = Cells(i, 1).Value
Next

Bon, ceci était pour reprendre ton code, qui ne correspond pas à la description de ce que tu veux faire.

Epilogue

A la lumière de ces très (trop ?) nombreux éléments, voudrais-tu reformuler ton besoin, et voire proposer un début de bout de code ?
Comme on a évoquer la collection Workbooks, gérer des cellules, des feuilles et donc même des classeurs ne devraient pas être si difficile ;)
 

cassivellaunos

Nouveau membre
Meilleure réponse sélectionnée par cassivellaunos.
 

zeb

Modérateur
Eh, non seulement t'as tout lu, mais en plus t'as tout compris et tu t'en es servi pour résoudre ton problème ? Ben c'est top ! :)
 

cassivellaunos

Nouveau membre

Bonjour ZEB et mille mercis,
Non je n'ai pas tout compris :??: , non mon Pb n'est pas résolu :ouch: (loin de là) mais oui j'ai tout lu :pt1cable: !
je pensais pouvoir le faire (userform) dans la foulée pendant mes vacances mais j'ai été appelé pour une mission urgente, d'où l'absence de réponse.
Je suis stupéfait de ta façon d'expliquer (didactique et pédagogique)! Je pense que je ne suis pas très doué car entre la lecture et l'écriture il y a un monde.
Je vais m'y remettre ce WE et je te tiens au courant. Ne m'en veux pas mais je suis un peu lent...
Encore merci pour tes explications.
Cassivellaunos
 

zeb

Modérateur
En choisissant mon deuxième message comme étant la meilleure réponse, tu as marqué ce sujet comme résolu. C'est pourquoi je te faisais cette remarque.

Je te laisse progresser à ton rythme ;)
 
Vous devez vous inscrire ou vous connecter pour répondre ici.
Derniers messages publiés
Statistiques globales
Discussions
730 098
Messages
6 717 042
Membres
1 586 281
Dernier membre
moto45ktm
Partager cette page
Haut