Commentaires

  1. Avatar de Malick
    • |
    • permalink
    Bonjour Pierre,

    Merci bien pour la contribution

    En te relisant encore, j'ai vérifié mon outil et je me rends compte que j'ai sûrement fait une bourde sur un autre script qui touche les tableaux structurés.

    En fait, en voulant importer le contenu d'un tableau se trouvant sur un autre classeur, j'ai utilisé ListObject, mais pour le collage des données, je suis passé par le range en me positionnant sur la première cellule de la Ligne 1 du tableau de destination. Cela fonctionne bien, mais j'imagine que ce n'est pas la bonne manière

    Pour ne pas polluer le fil, je posterai le code sur le forum VBA pour échanger dessus et le corriger

    encore pour l'apprentissage des bonnes manières que tu dispenses nb

    Malick
  2. Avatar de User
    • |
    • permalink
    Sympa ce billet !

    Je ne connaissais pas la formule.

    Merci Pierre
  3. Avatar de Pierre Fauconnier
    • |
    • permalink
    Citation Envoyé par curt
    [...]
    Salut Curt,

    Merci pour ton appréciation et bon weekend
  4. Avatar de Pierre Fauconnier
    • |
    • permalink
    Salut

    Citation Envoyé par ec
    Je ne sais pas si je sors du sujet, mais j'ai l'habitude de sélectionner ma colonne d’adresse mail dans Excel et de la coller directement dans la zone d'adresse du message. Ne pas avoir peur de la disposition dans Outlook, au bout de quelques secondes il dispose correctement les adresses.
    Oui, c'est aussi une astuce... Mais si on les souhaites filtrées, triées, uniques, ça demande x manipulations à refaire à chaque fois. Avec une formule, si tu veux d'abord avoir celles d'un domaine, puis celle d'un autre, il te suffit de modifier le domaine dans la cellule où tu l'auras mentionné. Ce sera la seule manipulation à réaliser pour obtenir une autre liste.

    De plus, j'ai pris ici des adresses mail à titre d'exemple, mais tu peux envisager ces fonctions pour d'autres situations. En formation, j'ai ainsi pu montrer que l'on pouvait choisir une série de factures à valider dans Excel sur base de leurs numéros, puis créer la liste avec le séparateur pour l'envoyer dans un champ de saisie d'un CRM pour une validation "à la volée". Ce qu'il faut retenir ici, c'est plus l'arrivée de nouvelles fonctions qui ouvrent à de nouvelles possibilités, sans trop se focaliser sur la spécificité d'adresses envoyées dans Outlook
  5. Avatar de curt
    • |
    • permalink
    Bonjour Pierre,

    merci pour l'info et le partage... et avec un tout petit peu de code et on peut envoyer un mail via Outlook en ayant sélectionné la liste de diffusion à partir d'Excel...
    Superbe travail.
    Curt
  6. Avatar de ec
    • |
    • permalink
    Je ne sais pas si je sors du sujet, mais j'ai l'habitude de sélectionner ma colonne d’adresse mail dans Excel et de la coller directement dans la zone d'adresse du message. Ne pas avoir peur de la disposition dans Outlook, au bout de quelques secondes il dispose correctement les adresses.
  7. Avatar de mouftie
    • |
    • permalink
    Bonjour Monsieur,

    Super travail, car peu de gens ont intégré les tables référencées dans leur pratique régulière ; je pense en grande partie à cause des références absolues et de l'abandon du $ avec la touche F4...

    Vos 2 tutos :
    Utilisation courante des tables
    https://fauconnier.developpez.com/tu...ructures/#LVII
    et
    Utilisation des table avec VBA
    https://fauconnier.developpez.com/tu...uresvba/#LII-A
    Sont super et très bien fait ;

    Mon seul regret est que vous n'ayez pas terminé celui pour VBA...

    Je profite de cet occasion pour vous félicitez de votre dévouement pour aider notre communauté.
  8. Avatar de Pierre Fauconnier
    • |
    • permalink
    Bonjour.

    Comme tu as aussi posé la question sur le forum, j'ai répondu dans cette discussion, notamment parce que je ne comprends pas bien le contexte dans lequel tu évolues... N'hésite pas à réagir dans la discussion...
  9. Avatar de elclor
    • |
    • permalink
    Bonjour
    Je ne connais rien du PQ mais tu m'as donné envie de commencer et pour cela j'essaye de refaire ton exemple.
    Mais dès le début en ajoutant une colonne avec la condition identique à la tienne=[@PAYS]="AU" je reçois le message d'erreur suivant : la syntaxe de ce nom est incorrecte et ce qui est dans les crochets est mis en surbrillance. Une colonne PAYS existe bien dans ma BD et AU comme valeur d'une cellule de cette colonne.
    Merci de ton aide
    elclor
  10. Avatar de apt
    • |
    • permalink
    Merci Pierre pour ces précieux conseils
  11. Avatar de Qwazerty
    • |
    • permalink
    Salut Pierre

    C'est une notion qui me semble essentielle.
    D'ailleurs la notation range("A1") est bien souvent trop juste également à mon gout et devrait être accompagnée à minima de la feuille si on utilise le CodeName et au mieux de l'ensemble Classeur.Feuille.Range.

    Je vois très souvent des codes proposés sur les forums qui négligent cette aspect là... et bien souvent ces mêmes codes sont mal structurés, car souvent issu de l'enregistreur, provoquant des lenteurs d'exécution.
    L'ensemble des deux (absence d'antécédent et lenteur) laisse tout loisir à l'utilisateur de changer de feuille voir de classeur durant l'exécution du code...
    Là où ThisWorkbook.Worksheet("Feuil1").Range("A1").Value = "Test" fait le boulot sans histoire, Range("A1").Value = "Test" met un beau bor*el dans la feuille et/ou le classeur passé(e) au premier plan.

    VB est très verbeux, les gens veulent souvent s'épargner du code ou du moins alléger leur code mais je pense que c'est bien souvent un mauvaise idée. (je passe sur l'utilisation des ":" pour tout assembler sur une ligne)
    Il est préférable, toujours à mon avis, d'utiliser une structure With ... End With ou encore utiliser une variable pour pointer la feuille ou le range sur lequel on veut travailler, on allège ainsi le code sans perdre en précision et sans prendre le risque de massacrer une autre feuille.
    Sachant qu'il n'est pas possible d'utiliser Ctrl+Z pour rattraper les dégâts et imaginez si en plus la macro fait un point de sauvegarde du classeur actif.....

    Je te souhaite une bonne soirée
    ++
    Qwaz
  12. Avatar de Pierre Fauconnier
    • |
    • permalink
    Salut.

    Perso, je ne suis pas un grand fan des dictionnaires. Ici, j'ai repris une fonction que j'utilise depuis longtemps en passant un tableau à une dimension (elle provient d'un framework MVC en PHP que j'ai créé il y a quelques années). On pourrait éventuellement utiliser un tableau à deux dimensions qui matérialise peut-être mieux les paires Ancienne valeur/Nouvelle valeur.

    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
    Public Function ReplaceStrings(Source As String, Parameters) As String
      Dim I As Long
     
      ReplaceStrings = Source
      For I = LBound(Parameters) To UBound(Parameters)
        ReplaceStrings = Replace(ReplaceStrings, Parameters(I, LBound(Parameters, 2)), Parameters(I, UBound(Parameters, 2)), vbTextCompare)
      Next I
    End Function
    
    Sub Test()
      Dim t(1, 1)
      Dim s As String
      
      t(0, 0) = "{Femme}"
      t(0, 1) = "Martine"
      t(1, 0) = "{Homme}"
      t(1, 1) = "Pierre"
    
      s = "{Femme} et {Homme}"
      s = ReplaceStrings(s, t)
    End Sub
    Toutefois, que ce soit avec un dico ou un tableau à deux dimensions, il faut passer par une variable pour alimenter le tableau. Un tableau à une dimension peut être peuplé comme je le montre dans le code initial avec Array(...), ce qui a l'avantage de la concision au niveau du code.

    EN VBA/Excel, on pourrait utiliser t = [{"{Femme}","Martine";"{Mari}","Pierre"}] pour créer le tableau à deux dimensions, mais je préfère que la fonction soit la plus générique possible. C'est pourquoi le tableau à une dimension offrant la possibilité d'utiliser la fonction Array a ma préférence.
  13. Avatar de Blahite
    • |
    • permalink
    Bonjour,

    Assez intéressant.
    Une raison particulière de ne pas utiliser les dictionnaires ?
    S'il y a beaucoup de paramètre cela simplifie le code et la lisibilité à première vue.
    Exemple ci-dessous (adaptation à l'arrache, il y a peut-être mieux à faire en terme de binding)

    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
    Public Function ReplaceStrings(Source As String, ByRef dict) As String
      Dim I As Long
      Dim key As Variant
     
      ReplaceStrings = Source
    
      For Each key In dict.Keys
        ReplaceStrings = Replace(ReplaceStrings, key, dict(key), 1, -1, vbTextCompare)
      Next key
    End Function
    
    Sub Test4()
      Dim Message As String
      Dim dict As Object
      Set dict = CreateObject("Scripting.Dictionary")
      
      dict("{depart}") = "Place du Perron,+4910+Theux"
      dict("{arrivee}") = "Place Verte,+4900+Spa"""
     
      Message = "<A href=""https://www.google.fr/maps/dir/{depart}/{arrivee}"">Cliquez ici pour la navigation.</A>"
      Message = ReplaceStrings(Message, dict)
    End Sub
  14. Avatar de MarcelG
    • |
    • permalink
    Salut Pierre,

    La déclaration de variables est pour moi, et je le mentionne souvent dans les discussions, fondamentale.

    Maintenant, il s'agit aussi, à mon avis, de bien effectuer cette déclaration.
    Je reprends souvent cet exemple des dictionnaires.

    En ayant activé la référence "Scripting.Runtime"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dim dico as Scripting.Dictionary
    Le développeur peut ensuite bénéficier de l'IntelliSense, autrement dit des propriétés et méthodes afférant à la variable.

    Je sais qu'il y a sujet à discussion. Mais c'est mon avis.

    A plus tard.

    Marcel
  15. Avatar de MarcelG
    • |
    • permalink
    Très bien.

    Je prends note, en ayant mis à jour mon aide-mémoire (créé en 2009! ) dans sa partie "Feuilles".

    Merci et à bientôt.

    Marcel
  16. Avatar de Pierre Fauconnier
    • |
    • permalink
    Salut Marcel.

    Merci pour tes commentaires et questions. Je vais les prendre à l'envers, si tu veux bien, et d'abord répondre à ta dernière question: Pourquoi As Object et pas As Worksheet?

    Pour que la fonction puisse aussi être utilisée pour une feuille de graphique (Objet Chart) et éviter ainsi d'avoir deux fonctions, une qui traite la feuille de calcul et l'autre qui traite la feuille de graphique.

    Pour ce qui est des codes de Philippe, je ne peux pas les commenter en faisant abstraction de On Error puisque sans les On Error, les codes proposés ne sont pas fonctionnels et que perso, je ne veux pas utiliser les On Error dans ces cas.

    La dernière proposée, qui n'utilise pas de boucle et pas de gestion d'erreurs pourrait être intéressante, mais elle est limitée aux feuilles de calcul. Dès lors, chaud partisan de la systématisation dans mes codes, je préfère en utiliser une seule qui permet de tester une feuille quel que soit son type, de manière à ne pas encombrer mon module xlTools avec des fonctions presqu'identiques*, et de ne pas devoir me poser la question du type de la feuille.

    Je peux juste proposer une "fusion" de mes deux fonctions (celle qui renvoie la feuille et celle qui teste qu'elle existe), bien que je n'aime pas trop cette solution qui oblige à passer un objet même si l'on souhaite seulement tester l'existence de la feuille.

    Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Function SetSheetByName(Name As String, ByRef sh As Object, Optional wb As Workbook) As Boolean
      Dim Counter As Long
     
      Set sh = Nothing
      If wb Is Nothing Then Set wb = ActiveWorkbook
      Counter = 1
      Do While Counter <= wb.Sheets.Count And sh Is Nothing
        If StrComp(wb.Sheets(Counter).Name, Name, vbTextCompare) = 0 Then Set sh = wb.Sheets(Counter)
        Counter = Counter + 1
      Loop
      SetSheetByName = Not sh Is Nothing
    End Function

    Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Sub testSheet()
      Dim sh As Object
     
      If SetSheetByName("Feuil1", sh) Then
        Debug.Print sh.Name
      Else
        MsgBox "La feuille n'existe pas"
      End If
    End Sub




    * En son temps, j'avais aussi créé une fonction qui permettait de rechercher une feuille selon son name ou son codename. Elle traîne toujours dans mon module Tools (jétais moins organisé à l'époque... )

    Code vba : 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
    Function getSheetByName(Optional Name As String, Optional CodeName As String, Optional wb As Workbook) As Object
      Dim sh As Object
      Dim Counter As Long
     
      If Name <> "" Or CodeName <> "" Then
        If wb Is Nothing Then Set wb = ActiveWorkbook
        Counter = 1
        Do While Counter <= wb.Sheets.Count And getSheetByName Is Nothing
          If Name <> "" Then
            If StrComp(wb.Sheets(Counter).Name, Name, vbTextCompare) = 0 Then Set getSheetByName = wb.Sheets(Counter)
          Else
            If StrComp(wb.Sheets(Counter).CodeName, CodeName, vbTextCompare) = 0 Then Set getSheetByName = wb.Sheets(Counter)
          End If
          Counter = Counter + 1
        Loop
      End If
    End Function

    Elle s'appelle facilement avec les arguments nommés (que j'utilise très souvent dans mes codes, par ailleurs).

    Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Sub TestSheets()
      Debug.Print getSheetByName(Name:="Feuil2").CodeName
      Debug.Print getSheetByName(CodeName:="shTest").Name
    End Sub
  17. Avatar de MarcelG
    • |
    • permalink
    Dans les solutions précédentes, beaucoup - trop à mon goût - de gestions d'erreurs. Ce dont il faut user, certes, mais à bon escient. Personnellement, j'évite tant faire se peut.
    Cela dit, pas de sortie de bloc anticipée.

    Je mets donc ce billet dans mes priorités

    Ce tout en sachant que l'on peut, sauf erreur, obtenir, comme demandé la plupart du temps, une fonction booléenne sur getSheetByName = Nothing ou non.

    Du type:

    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
    Function ExisteFeuille(Name As String, Optional wb As Workbook) As Boolean
      
    Dim getSheetByName As Object
    Dim sh As Object
    Dim Counter As Long
    
    If wb Is Nothing Then Set wb = ActiveWorkbook
            Counter = 1
            Do While Counter <= wb.Sheets.Count And getSheetByName Is Nothing
            If StrComp(wb.Sheets(Counter).Name, Name, vbTextCompare) = 0 Then Set getSheetByName = wb.Sheets(Counter)
            Counter = Counter + 1
    Loop
    
    If getSheetByName Is Nothing Then
            ExisteFeuille = False
    Else
            ExisteFeuille = True
            Set getSheetByName = Nothing
    End If
    
    End Function
    Maintenant, je pose la question, Pierre.

    J'ai codé cette fonction en déclarant sh en Worksheet.
    La fonction reste bien effective. Une feuille étant un objet.

    Y a-t-il une raison à la déclaration initiale en Object?
    Il y a sans doute là des notions qui me sont étrangères.

    A plus tard.

    Bonne soirée.
    Mis à jour 20/11/2019 à 10h12 par MarcelG
  18. Avatar de MarcelG
    • |
    • permalink
    Salut Pierre,

    Sans flagornerie aucune.
    Intéressant

    A retenir en règle générale. Pas de Exit mais plutôt Do..While (ou Until sans doute)
    Si l'on m'y reprend (car je suis souvent tombé), une bière pour Marcel!

    Je reporte ci-dessous un extrait de mes aides-mémoires.
    (dont quelques liens de Philippe)
    Peut-être un avis? (les On Error, que tu mentionnes dans ton billet, mis à part)

    A plus tard.

    http://www.developpez.net/forums/d13...xcel-avec-vba/
    Function ExistSheet(Ws$, Optional Wb As Workbook) As Boolean
    If Wb Is Nothing Then Set Wb = ActiveWorkbook
    On Error Resume Next
    ExistSheet = IsObject(Wb.Sheets(Ws))
    End Function

    Voir les liens de Philippe Thulliez

    https://www.developpez.net/forums/d1.../#post10992616

    2 premières fonction = Philippe Thulliez

    Function IsSheetExist(SheetName As String, Optional Wkb As Workbook) As Boolean
    ' http://philippe.tulliez.be
    ' Renvoie True ou False
    ' Arguments
    ' SheetName (String) Nom de la feuille dont on teste l'existence
    ' [wkb] (Workbook) ThisWorkbook est le classeur par défaut
    Dim Sht As Worksheet
    If Wkb Is Nothing Then Set Wkb = ThisWorkbook
    On Error Resume Next
    Set Sht = Wkb.Sheets(SheetName)
    With Err
    If .Number Then .Clear Else IsSheetExist = True
    End With
    ‘--------------------------------

    http://philippe.tulliez.be/fonction-...s-un-classeur/

    Function IsSheetExist(Name As String) As Boolean
    ' Cette fonction renvoie TRUE si la feuille existe et FALSE dans le cas contraire
    ' Argument
    ' Name (String) - Nom de la feuille dont on teste l'existence
    On Error Resume Next
    IsSheetExist = Len(Sheets(Name).Name) > 0
    On Error GoTo 0
    End Function

    Function FeuilleExiste(NomFeuille) As Boolean
    Dim f As Object
    On Error Resume Next
    Set f = Sheets(NomFeuille)
    If Err = 0 Then FeuilleExiste = True
    Set f = Nothing
    End Function
    ‘--------------------------------
    http://www.mdf-xlpages.com/modules/s...q.php?faqid=40
    Function FeuilExiste(F As String) As Boolean
    On Error Resume Next
    FeuilExiste = Not Sheets(F) Is Nothing
    End Function
    ‘--------------------------------
    https://www.developpez.net/forums/d1...feuille-excel/
    Public Function WorkSheetExist(Sheetname As String) As Boolean

    On Error Resume Next
    WorkSheetExist = Sheets(Sheetname).Index
    End Function
    ‘--------------------------------
    Function sheetExists(Feuille) As Boolean ' Code Fait par Marc-L
    sheetExists = Evaluate("ISREF('" & Feuille & "'!A1)")
    End Function
    ‘‘--------------------------------
    Mis à jour 19/11/2019 à 21h01 par MarcelG
  19. Avatar de Guillaume.Chevallier
    • |
    • permalink
    Bonjour,

    Ce matin en relisant le premier billet de la série, j'ai vu tout en bas de page le lien vers le billet "VBA: De la bonne programmation d'un userform".
    Celui-ci répond effectivement à ma question sur la validation des données. La distinction entre validation technique et validation métier est bien compréhensible.

    Je vais d'abord m'attaquer à la validation technique, même si j'essaie au maximum de guider l'utilisateur avec des listes.

    Vos billets sont clairs et concis, ils sont facilement compréhensible. C'est un plaisir de vous lire.


    Guillaume.
  20. Avatar de Pierre Fauconnier
    • |
    • permalink
    Bonsoir Guillaume,

    Enchanté que ces billets vous plaisent et vous aident à développer votre solution ,)

    Qu'entendez-vous par "validation des formulaires"?

    Pour moi, il y a deux types de validation, la validation technique et la validation métier.

    Par validation technique, j'entends le fait de vérifier que si l'on attend une date comme saisie dans un contrôle, ce soit bien une date qui a été saisie. Par validation métier, j'entends le fait que si l'on attend une date comprise dans une période donnée, ce soit bien une date de cette période qui ait été saisie. Je détaille une façon de réaliser cela dans ce billet.

    Pour Access, le problème réside dans le fait que c'est une application tierce à Excel qui va éventuellement rejeter l'erreur. Comme exemples, je citerai évidemment
    • l'intégrité référentielle qui, en dernier ressort, est vérifiée par Access;
    • les macros de données qui pourraient rejeter telle ou telle valeur;
    • les contraintes de validité qui interdiraient un Null;
    • ...


    J'exclus, temporairement du moins, les erreurs dues à des problèmes de type de données, car le passage par des classes personnalisées comme montré dans les billets réduisent normalement le risque d'erreur. On pourrait toutefois gérer les erreurs à ce niveau-là, en préventif comme en curatif.

    Je pense que l'application Excel qui est développée sur base Access doit prévenir autant que possible les problèmes qui pourraient arriver côté Access. Il me semble raisonnable de penser que le développeur Excel est aussi le concepteur de la base Access, de sorte qu'il peut intégrer, soit dans sa BL soit dans sa DAL les règles qui prévalent côté Access.

    Reste que de toute façon, il faut prévoir le rejet par Access, qui se manifestera par la levée d'une exception qui devra être gérée au niveau de la DAL Excel par une valeur de retour des fonctions de la DAL. A charge pour l'appli Excel de gérer ces retours et au programmeur de prévoir que cela remonte jusqu'au userform (de la DAL vers la PL en passant, en trois tiers pur, par la BL). Normalement, les On Error, soit au niveau de la DAL pour capturer l'exception et l'écrire dans un fichier de log, soit en laissant remonter l'erreur jusqu'au userform, sont les instruments qu'il faudra mettre en place pour gérer les rejets d'Access.

    Je vais suivre votre conseil et écrire un cinquième billet ou je modéliserai les trois cas envisagés ici (rejet technique au niveau du formulaire, rejet métier au niveau Excel et rejet par Access géré par les On Error, ainsi que la gestion en préventif ou en curatif au niveau des objets). Je vais essayer d'écrire ce billet dans le début de la semaine prochaine.
Page 1 sur 4 1234 DernièreDernière