Excel - Liens relatifs
Excuse-moi mais on a du mal à le croire vu la façon dont tu continues de raisonner...
Bonjour,
Et pourtant j'ai bien compris ! Dans le cas inverse le sujet aurait été cloturé depuis plusieurs postes en vous remerciant que cela marche.
Le code fonctionne, mais je me rends bien compte que la manière dont je l'ai implémenté n'est pas correct (même si pour l'instant je n'en trouve aucune autre).
J'ai bien compris qu'une macro peut être exécuté sur n'importe quelle feuille, sauf que toutes les feuilles ne se ressemblent pas, et une macro ne peut, par exemple ne pas fonctionner sur une feuille.
Voici actuellement ce que j'ai mis en place.
Une macro en public. Elle est appelé lorsque je clique sur le bouton de la Feuil3 (dans ce cas il y a une MsgBox pour confirmer) et lorsque je clique sur le bouton de la Feuil1 (et dans ce cas la macro s'exécute pour la Feuil3 alors que je suis sur la Feuil1). Pour pouvoir l'exécuter, et suivant ton conseil de bannir les selects, j'ai modifié par cela :
Sheets("Liste Membres AG Perm & Temp").Activate
Call Feuil3.refresh
Sheets("Tous les membres").ActivateCela est peut être aussi malpropre que les selects ?!
J'ai regardé si les fonctions serait une manière d'implémenter cette fonctionnalité d'une meilleure manière sauf que cela ne correspond pas à mes attentes.
De plus j'ai fait en sorte d'améliorer le code de ma macro pour que celle-ci ne renvoie pas de message d'erreur dans le cas où elle est malencontreusement exécuté pour une autre Feuil que la Feuil3.
D'ailleurs je suis en train de me dire, ce code "de sécurité" je peux tout simplement le modifier, donc comme cela, lorsque j'exécute la macro depuis n"importe quelle Feuille, je redirige son exécution vers la Feuil3, à la place d'ignorer l'exécution si la feuille n'est pas bonne. Dans ce cas, je serais sûr que c'est bien sur la Feuil3 que celle-ci s'exécute à chaque fois qu'on l'appelle.
Encore merci de vos réponses !
Bonjour,
HyperNovax a écrit :Une macro en public. Elle est appelé lorsque je clique sur le bouton de la Feuil3 (dans ce cas il y a une MsgBox pour confirmer) et lorsque je clique sur le bouton de la Feuil1 (et dans ce cas la macro s'exécute pour la Feuil3 alors que je suis sur la Feuil1). Pour pouvoir l'exécuter, et suivant ton conseil de bannir les selects, j'ai modifié par cela :
Sheets("Liste Membres AG Perm & Temp").Activate Call Feuil3.refresh Sheets("Tous les membres").ActivateCela est peut être aussi malpropre que les selects ?!
si la méthode est bien dans la feuille "Feuil3" et que la fonction est en public ("public function") alors il suffit de d'affecter à ton buton de "Feuil1" :
sub macro1()
Feuil3.refresh
end sub Merci, cela fonctionne, je pensais qu'une fonction devait obligatoirement renvoyer quelque chose.
Mais par contre cela n'enlève pas le fait qu'il faut que je change de feuille pour que la macro s'exécute sur la Feuil3 !
tout dépend de comment vous avez déclarer la fonction dans la feuille , pour ma part ma fonction de teste est la suivante :
feuil2
Public Function affichage()
Range("A1").Value = "succès"
End Function le "Range" renvoie bien au range de la feuille 2 et non de la feuille 1
bouton feuil1 :
Sub Test()
Feuil2.affichage
End Subici nul besoin d'activer la feuille 2
En effet cela fonctionne, du moins partiellement. En effet le soucis se situe maintenant dans mon code. Dès qu'il y a un select ou un ActiveSheet cela ne fonctionne plus.
Par exemple, pour pouvoir actualiser mon tableau je dois au préalable supprimer toutes ces valeurs, redimensionner le tableau pour ensuite ajouter les valeurs les plus récentes. La suppression des valeurs fonctionnent mais le redimensionnement du tableau non (par cette méthode d'appel du moins).
'--- Efface les valeurs du tableau ---
Range("Tableau_membres_perm_temp[Civilité]").ClearContents
Range("Tableau_membres_perm_temp[Nom]").ClearContents
Range("Tableau_membres_perm_temp[Prénom]").ClearContents
Range("Tableau_membres_perm_temp[Fonction]").ClearContents
Range("Tableau_membres_perm_temp[Titre]").ClearContents
Range("Tableau_membres_perm_temp[Nom complet]").ClearContents
Range("Tableau_membres_perm_temp[Ville]").ClearContents
Range("Tableau_membres_perm_temp[Typologie Convention Constitutive]").ClearContents
Range("Tableau_membres_perm_temp[Membre de l''Assemblée Générale]").ClearContents
Range("Tableau_membres_perm_temp[Membre temporaire de l''Assemblée Générale]").ClearContents
ActiveSheet.ListObjects("Tableau_membres_perm_temp").Resize Range("$A$4:$P$5") ' <======MFerrand a écrit :
Hé bé !
Je devine qu'a tes yeux je n'ai toujours rien compris ?! Hé bé
pourquoi utiliser le "avtivesheets" alors que la fonction est a appliquer sur la feuille 3 au final (vu que le code est présent dans la feuille) ? si vous voulez utiliser un fonction sur toute les feuille il faut pas mettre la macro dans une feuille mais dans un module
minanse a écrit :pourquoi utiliser le "avtivesheets" alors que la fonction est a appliquer sur la feuille 3 au final (vu que le code est présent dans la feuille) ? si vous voulez utiliser un fonction sur toute les feuille il faut pas mettre la macro dans une feuille mais dans un module
Ok, c'est juste que je m'étais aidé de l'enregistreur de macro pour réaliser cette action. Il m'a donc mis ActiveSheet !
Sinon c'est bien une fonction propre à la feuille comme je l'ai décrite !
HyperNovax a écrit :MFerrand a écrit :
Hé bé ! Je devine qu'a tes yeux je n'ai toujours rien compris ?! Hé bé
Je ne vais pas m'étendre, je n'en ai guère le temps...
Ton sujet ouvre la voie à plusieurs débats de natures différentes :
Evidemment remplacer Select par Activate, c'est du pareil au même...
De la même façon ActiveSheet ou ActiveCell ne qualifient pas de façon totalement garantie une expression et sont à éviter dans le cas général. On ne l'utilise à coup sûr que dans des cas précis : ActiveSheet dans un code lancé par un bouton sur une feuille, on sait qu'au départ cette feuille sera la feuille active, puisqu'il faut aller sur la feuille pour cliquer sur le bouton (mais à condition qu'on ne puisse pas lancer la macro autrement que par ce bouton...)
Ceci n'est qu'un volet, volet connexe : tu sembles avoir placé une procédure nommé refresh dans un module de feuille... Dès lors qu'elle ne relève pas d'un évènement ou y est liée, c'est évidemment déconseillé. La place des procédures standards est dans un module standard... Tu as en plus donné un nom qui est un nom de méthode s'appliquant à un bon nombre d'objets ! (par chance pour toi, elle ne s'applique pas à l'objet Worksheet...), ce qui est tout autant déconseillé pour éviter des surprises par la suite...
Mais le débat principal que tu as en fait lancé porte sur la notion même de macro : une macro c'est du code, peu importe ce qui le lance et comment, une fois qu'il est lancé il s'exécute, il doit donc se suffire à lui-même pour faire ce qu'on en attend. Il ignore s'il doit agir sur Feuil1, Feuil3, ou un autre classeur, ou même hors d'Excel, il agit sur ce que le code vise explicitement, et si on ne lui fournit pas d'objet précis, il agit sur l'objet qui semble être le bon ou s'il ne peut mener l'action sur cet objet, se met en erreur...
C'est ce que tu sembles avoir du mal à assimiler : que c'est dans le code qu'il te faut te faut définir les objets sur lesquels tu agis, de même que l'action à réaliser...
Pour les détails, oui une procédure Function renvoie un résultat, et il est idiot d'écrire une telle procédure pour ne pas lui faire renvoyer de résultat ! Mais elle est utilisée comme fonction quand elle est à droite d'un signe =. Elle peut être utilisée sans récupérer son résultat dans les mêmes conditions qu'une procédure Sub...
Bref ! tous ces éléments font partie de l'ABC de l'apprentissage de VBA, on ne peut faire l'impasse sur eux pour pouvoir écrire un code qui tienne la route... Il convient de commencer par s'affranchir de l'usage de l'enregistreur pour cesser de produire un code par définition mauvais.
Cordialement.
L'enregistreur de macro utilise les ActiveSheet / Select. J'ai changé en conséquence et voilà ce que cela donne maintenant !
Ça fonctionne bien ! Merci !
Voici le résultat final en appliquant le tuto ci-dessus !
Public Function refresh()
' Gèle l'écran pour éviter de voir les étapes de l'actualisation
Application.ScreenUpdating = False
'--- Efface les valeurs du tableau ---
Range("Tableau_membres_perm_temp").ClearContents
ListObjects("Tableau_membres_perm_temp").Resize Range("$A$4:$K$5")
'--- Variables ---
Dim Cn As ADODB.Connection
Dim Fichier As String
Dim NomFeuille As String, texte_SQL As String
Dim Rst As ADODB.Recordset
'-----------------
'--- Classeur source ---
Fichier = ThisWorkbook.Path & "\membres_AG.xlsm"
NomFeuille = "Tous les membres"
Set Cn = New ADODB.Connection
'-----------------------
'--- Connexion ---
With Cn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" _
& Fichier & ";Extended Properties=""Excel 12.0;HDR=YES;"""
.Open
End With
'-----------------
'--- Définit la requête ---
texte_SQL = "SELECT `Civilité`, `Nom`, `Prénom`, `Fonction`, `Titre`, `Nom complet`, `Ville`, `Typologie Convention Constitutive`, `Membre de l'Assemblée Générale`, `Membre temporaire de l'Assemblée Générale` FROM [" & NomFeuille & "$] WHERE `Membre de l'Assemblée Générale` = 'Oui' OR `Membre temporaire de l'Assemblée Générale` = 'Oui'"
Set Rst = New ADODB.Recordset
Set Rst = Cn.Execute(texte_SQL)
'--------------------------
'--- Ecrit le résultat de la requête dans la cellule A5 ---
Range("B5").CopyFromRecordset Rst
'--- Fermeture connexion ---
Cn.Close
Set Cn = Nothing
'--- Gestion de l'affichage des colonnes ---
Columns("A:A").EntireColumn.AutoFit
Columns("B:B").EntireColumn.AutoFit
Columns("C:C").EntireColumn.AutoFit
Columns("D:D").EntireColumn.AutoFit
Columns("E:E").EntireColumn.AutoFit
Columns("F:F").EntireColumn.AutoFit
Columns("G:G").EntireColumn.AutoFit
Columns("H:H").EntireColumn.AutoFit
Columns("I:I").EntireColumn.AutoFit
Columns("J:J").EntireColumn.AutoFit
Columns("K:K").EntireColumn.AutoFit
Columns("A:A").EntireColumn.Hidden = True
'-------------------------------------------
End FunctionMFerrand a écrit :Bref ! tous ces éléments font partie de l'ABC de l'apprentissage de VBA, on ne peut faire l'impasse sur eux pour pouvoir écrire un code qui tienne la route... Il convient de commencer par s'affranchir de l'usage de l'enregistreur pour cesser de produire un code par définition mauvais.
Cordialement.
Sur la majorité des postes que j'ai pu lire, ils conseillent d'utiliser l'enregistreur de macro pour partir sur une bonne base et voir ce qui est exécuté. Après il est clair qu'il faut repasser par dessus, j'en ai fait l'expérience.
Je pense quand même que l'enregistreur de macro possède une utilité mais quand on pousse un peut la chose cela en devient moins pertinent ?!
MFerrand a écrit :Ceci n'est qu'un volet, volet connexe : tu sembles avoir placé une procédure nommé refresh dans un module de feuille... Dès lors qu'elle ne relève pas d'un évènement ou y est liée, c'est évidemment déconseillé. La place des procédures standards est dans un module standard... Tu as en plus donné un nom qui est un nom de méthode s'appliquant à un bon nombre d'objets ! (par chance pour toi, elle ne s'applique pas à l'objet Worksheet...), ce qui est tout autant déconseillé pour éviter des surprises par la suite...
La procédure refresh est placé dans le module de la Feuil3. Dans un premier temps le code était situé dans l’événement click du bouton. Sauf qu'après j'ai eu besoin d'appeler ce code à partir d'une autre feuille, d'où mon besoin de créer une fonction !
Après il est clair que le nom est mal choisi.
Sur la majorité des postes que j'ai pu lire, ils conseillent d'utiliser l'enregistreur de macro pour partir sur une bonne base et voir ce qui est exécuté. Après il est clair qu'il faut repasser par dessus, j'en ai fait l'expérience.
Eh bien ! je conseille très exactement l'inverse !
L'usage répété de l'enregistreur est le moyen idéal pour ne faire strictement aucun progès en VBA.
Le seul intérêt quand on débute est de passer le temps nécessaire sur un code enregistré, pour le comprendre ligne par ligne et se mettre en mesure de le réécrire autrement...
Ce qui ne dispense pas de prendre connaissance des chapitres de base concernant le langage !
Lorsqu'on a assimilé un tant soit peu les notions de portée des variables et des procédures, on ne déclare plus Public une procédure parce qu'elles le sont toutes par défaut, de même qu'on ne déclare plus Private une variable parce que pareillement elles le sont toutes par défaut...
Bonjour,
en faite pour le problème d'appel de fonction, pourquoi se prendre la tête à mettre des macros dans des feuille précise, mettre tout les fonction dans une(ou plusieurs) module(s) et voila 8)
Je pense que son utilité se trouve aussi dans la manière de réaliser une action. Je m'explique, même avec les cours de VBA, il n'est pas possible de connaitre toute les variables / objet et méthodes par cœur. L'enregistreur de macro permet de donner une piste de réflexion sur comment réaliser une tâche. Après je me trompe peut être.
Oui, mais personnellement je ne considère pas les différentes déclarations comme quelque chose de très gênant. Après tu as surement pris un exemple simple mais avoir deux déclarations différentes pour le même résultat, dans le cas où on affecte celle qui n'est pas par défaut, c'est dans tout les cas explicite. C'est sûr qu'en générale le "codeur est fainéant", donc pourquoi taper 5 caractères de plus alors que sans les taper le résultat est le même et la lisibilité n'en souffre pas ! Je range plutôt cela dans la qualité de lecture du code, avec le fait de commenter, de bien aérer les blocs. Il y a différentes manières de faire qui seront tout autant lisible.
je ne suis pas contre ce que tu dit HyperNovax mais quelque ligne(caractère) en plus signifie que le temps d'exécution qui ralentie aussi
quand on code , on essaye d'optimiser le code ( un minimum de code dit "inutile" a exécuté)
Je pense que son utilité se trouve aussi dans la manière de réaliser une action. Je m'explique, même avec les cours de VBA, il n'est pas possible de connaitre toute les variables / objet et méthodes par cœur. L'enregistreur de macro permet de donner une piste de réflexion sur comment réaliser une tâche. Après je me trompe peut être.
Ceci est en effet exact !
Il convient donc de combiner ces apports avec l'aide et avec l'utilisation de l'explorateur d'objets qui permet de pallier des insuffisances de l'aide (énumérations qui n'y figurent pas) ou retrouver des paramètres que l'enregistreur ne reproduit pas (ça arrive ! bien qu'il en fournisse généralement plus que nécessaire qu'il faut épurer, il y a parfois des choses que l'enregistreur omet, oubli de mIcrosoft sans doute...)
Pour la fin de ton propos, je ne partage pas ton avis !
S'agissant des déclarations, pour les procédure on a Sub ou Function qui indique sa nature (il y en a d'autres mais restons dans les élémentaires) si on n'a rien devant, on sait qu'on est en situation normale procédure publique), sinon on a Private qui attire l'attention sur le caractère particulier. Public attire faussement l'attention et est plutôt une gêne à la lecture.
Pour les variables l'alternance visuelle se fait entre Dim (variable ordinaire donc privée) et Public (variable publique), là c'est Private qui perturbe la lecture...
Par contre, je n'ai rien contre ne pas se fier aveuglément aux propriétés par défaut des objet, et j'ajoute .Value là où cela me permettra d'identifier plus vite que je fais appel à la valeur et non à l'objet. Je n'utilise quasiment jamais Sheets, au profit de Worksheets (plus précis et ne risquant pas de déclencher d'erreur sur la nature de l'objet)...
Sauter des lignes à l'intérieur d'une procédure ne facilite en rien la lecture (ça oblige souvent à scroller plus que nécessaire, et ça réduit la lisibilité produite par l'indentation), ça fait plutôt brouillon. Si on a des parties qu'on veut identifier ou distinguer rapidement, on intercale une ligne de commentaire (sans abuser des commentaires car cela parasite la lecture du code, on finit par devoir chercher le code au mileu de commentaires !)
Et l'indentation doit mettre l'alignement principal en retrait de la marge, de façon qu'entre Sub et End Sub (ou Function et End Function) il n'y ait sur le même alignement que les étiquettes de branchement éventuelles (qu'on identifie ainsi immédiatement). Et les retraits successifs doivent permettre d'indentier immédiatement les blocs d'instructions liés : quand j'ai un With mon regard descend immédiatement sur la colonne trouver le End With pour visualiser la dimension du bloc, quand j'ai un If je descends jusqu'au End If et je ne dois trouver entre sur le même alignement que ElseIf ou Else...
Ce n'est évidemment pas le modèle de ce qu'on traite le plus fréquemment sur le forum !