web-dev-qa-db-fra.com

Copier une feuille et obtenir un objet de feuille résultant?

Existe-t-il un moyen easy/short d'obtenir l'objet Excel.worksheet de la feuille new que vous obtenez lorsque vous copiez une feuille de calcul? 

ActiveWorkbook.Sheets("Sheet1").Copy after:=someSheet

Il s'avère que la méthode .Copy renvoie un booléen au lieu d'un objet de feuille de calcul. Sinon, j'aurais pu faire:

set newSheet = ActiveWorkbook.Sheets("Sheet1").Copy after:=someSheet    <-- doesn't work

J'ai donc écrit environ 25 lignes de code pour obtenir l'objet (lister toutes les feuilles avant la copie, lister toutes les feuilles après, et trouver celle qui ne figure que dans la dernière liste. Toutes très longues en VBA), mais je cherche une solution plus élégante et plus courte.

25
Rabarberski
Dim sht 

With ActiveWorkbook
   .Sheets("Sheet1").Copy After:= .Sheets("Sheet2")
   Set sht = .Sheets(.Sheets("Sheet2").Index + 1)
End With
23
Tim Williams

Je crois avoir enfin résolu le problème. Cela m'a rendu fou aussi! Ce serait vraiment bien si MS faisait copier par Copy un objet de feuille identique à la méthode Add ...

Le problème est que l’index attribué par VBA à une feuille récemment copiée n’est en fait pas déterminé ... comme d’autres l'ont déjà noté, cela dépend beaucoup des feuilles cachées. En fait, je pense que l'expression Sheets (n) est en fait interprétée comme "la nième feuille visible". Par conséquent, à moins d'écrire une boucle testant la propriété visible de chaque feuille, son utilisation dans le code présente un danger, à moins que le classeur ne soit protégé afin que les utilisateurs ne puissent pas jouer avec la propriété visible des feuilles. Trop dur...

Ma solution à ce dilemme est la suivante:

  1. Rendre visible la dernière feuille (même temporaire)
  2. Copier APRÈS cette feuille. Il DOIT avoir un index Sheets.Count
  3. Cachez à nouveau l'ancienne feuille précédente, si nécessaire - elle aura désormais Index Sheets.Count-1
  4. Déplacez la nouvelle feuille là où vous le souhaitez vraiment.

Voici mon code - qui semble maintenant être à l'épreuve des balles ...

Dim sh as worksheet
Dim last_is_visible as boolean

With ActiveWorkbook
    last_is_visible = .Sheets(.Sheets.Count).Visible
    .Sheets(Sheets.Count).Visible = True
    .Sheets("Template").Copy After:=.Sheets(Sheets.Count)
    Set sh=.Sheets(Sheets.Count)
    if not last_is_visible then .Sheets(Sheets.Count-1).Visible = False 
    sh.Move After:=.Sheets("OtherSheet")
End With

Dans mon cas, j'avais quelque chose comme ça (H indiquant une feuille cachée)

1 ... 2 ... 3 (H) ... 4 (H) ... 5 (H) ... 6 ... 7 ... 8 (H) ... 9 (H)

.Copie après: =. Sheets (2) crée une nouvelle feuille AVANT la feuille suivante VISIBLE - c’est-à-dire qu’il est devenu le nouvel index 6. PAS à l’index 3, comme on pouvait s’y attendre.

J'espère que cela pourra aider ;-) 

11
Trevor Norman

Une autre solution que j'ai utilisée serait de copier la feuille dans un endroit où vous connaissez son index, c'est-à-dire son premier. Là, vous pouvez facilement y trouver une référence pour tout ce dont vous avez besoin, puis vous pouvez le déplacer librement où vous le souhaitez.

Quelque chose comme ça:

Worksheets("Sheet1").Copy before:=Worksheets(1)
set newSheet = Worksheets(1)
newSheet.move After:=someSheet
7
Joubarc

METTRE À JOUR:

Dim ThisSheet As Worksheet
Dim NewSheet As Worksheet
Set ThisSheet = ActiveWorkbook.Sheets("Sheet1")
ThisSheet.Copy
Set NewSheet = Application.ActiveSheet
6
PaulStock

Je me rends compte que cet article date de plus d'un an, mais je suis venu ici pour chercher une réponse au même problème concernant la copie de feuilles et les résultats inattendus causés par des feuilles cachées. Rien de ce qui précède ne convenait vraiment à ce que je voulais principalement à cause de la structure de mon cahier de travail. Essentailly a un très grand nombre de feuilles et ce qui est affiché est déterminé par un utilisateur qui sélectionne une fonctionnalité spécifique. De plus, l'ordre des feuilles visibles était important pour moi, donc je ne voulais pas jouer avec celles-ci. Ma solution finale consistait donc à utiliser la convention de nommage par défaut d'Excel pour les feuilles copiées et à renommer explicitement la nouvelle feuille par son nom. Échantillon de code ci-dessous (à part, mon cahier a 42 feuilles et seules 7 sont visibles en permanence, et le after:=Sheets(Sheets.count) place ma feuille copiée au milieu des 42 feuilles, en fonction des feuilles visibles au début. temps.

        Select Case DCSType
        Case "Radiology"
            'Copy the appropriate Template to a new sheet at the end
            TemplateRAD.Copy after:=Sheets(Sheets.count)
            wsToCopyName = TemplateRAD.Name & " (2)"
            'rename it as "Template"
            Sheets(wsToCopyName).Name = "Template"
            'Copy the appropriate val_Request to a new sheet at the end
            valRequestRad.Copy after:=Sheets(Sheets.count)
            'rename it as "val_Request"
            wsToCopyName = valRequestRad.Name & " (2)"
            Sheets(wsToCopyName).Name = "val_Request"
        Case "Pathology"
            'Copy the appropriate Template to a new sheet at the end
            TemplatePath.Copy after:=Sheets(Sheets.count)
            wsToCopyName = TemplatePath.Name & " (2)"
            'rename it as "Template"
            Sheets(wsToCopyName).Name = "Template"
            'Copy the appropriate val_Request to a new sheet at the end
            valRequestPath.Copy after:=Sheets(Sheets.count)
            wsToCopyName = valRequestPath.Name & " (2)"
            'rename it as "val_Request"
            Sheets(wsToCopyName).Name = "val_Request"
    End Select

Quoi qu'il en soit, posté juste au cas où il serait utile à quelqu'un d'autre

3
Mark Moore

Mis à jour avec les suggestions de Daniel Labelle:

Pour gérer d'éventuelles feuilles masquées , rendez la feuille source visible, copiez-la, utilisez la méthode ActiveSheet pour renvoyer la référence à la nouvelle feuille et réinitialisez les paramètres de visibilité:

Dim newSheet As Worksheet
With ActiveWorkbook.Worksheets("Sheet1")
    .Visible = xlSheetVisible
    .Copy after:=someSheet
    Set newSheet = ActiveSheet
    .Visible = xlSheetHidden ' or xlSheetVeryHidden
End With
2
Rachel Hettinger

Cela devrait être un commentaire en réponse à @TimWilliams, mais c'est mon premier post donc je ne peux pas commenter.

Voici un exemple du problème mentionné par @RBarryYoung, lié aux feuilles masquées. Il y a un problème lorsque vous essayez de placer votre copie après la dernière feuille et que celle-ci est masquée. Il semble que, si la dernière feuille est masquée, elle conserve toujours l’index le plus élevé;

Dim sht As Worksheet

With ActiveWorkbook
   .Sheets("Sheet1").Copy After:=.Sheets(.Sheets.Count)
   Set sht = .Sheets(.Sheets.Count - 1)
End With

Situation similaire lorsque vous essayez de copier avant une première feuille masquée.

2
alrm3000

Il est exact que les feuilles de calcul masquées font en sorte que le nouvel index de la feuille de calcul ne soit pas séquentiel de part et d'autre de la feuille de calcul source. J'ai trouvé que la réponse de Rachel fonctionnait si vous copiez avant. Mais vous devrez l’ajuster si vous copiez après.

Une fois que le modèle est visible et copié, le nouvel objet de la feuille de calcul est simplement la feuille ActiveSheet, que vous copiiez la source avant ou après.

De préférence, vous pouvez remplacer: 

"Définissez newSheet = .Previous" avec "Définissez newSheet = Application.ActiveSheet".

J'espère que cela sera utile à certains d'entre vous.

1
Daniel Labelle

Basé sur la méthode de Trevor Norman , j'ai développé une fonction permettant de copier une feuille et de renvoyer une référence à la nouvelle feuille.

  1. Afficher la dernière feuille (1) si elle n'est pas visible
  2. Copier la feuille source (2) après la dernière feuille (1)
  3. Définissez la référence à la nouvelle feuille (3), c’est-à-dire après la dernière feuille (1)
  4. Masquer la dernière feuille (1) si nécessaire

Code:

Function CopySheet(ByRef sourceSheet As Worksheet, Optional ByRef destinationWorkbook As Workbook) As Worksheet

    Dim newSheet As Worksheet, lastSheet As Worksheet
    Dim lastIsVisible As Boolean

    If destinationWorkbook Is Nothing Then Set destinationWorkbook = sourceSheet.Parent

    With destinationWorkbook
        Set lastSheet = .Worksheets(.Worksheets.Count)
    End With

    lastIsVisible = lastSheet.Visible
    lastSheet.Visible = True

    sourceSheet.Copy After:=lastSheet
    Set newSheet = lastSheet.Next

    If Not lastIsVisible Then lastSheet.Visible = False

    Set CopySheet = newSheet

End Function

Cela insérera toujours la feuille copiée à la fin du classeur de destination.

Après cela, vous pouvez faire n'importe quels mouvements, renommer, etc.

Usage:

Sub Sample()

    Dim newSheet As Worksheet

    Set newSheet = CopySheet(ThisWorkbook.Worksheets("Template"))

    Debug.Print newSheet.Name

    newSheet.Name = "Sample" ' rename new sheet
    newSheet.Move Before:=ThisWorkbook.Worksheets(1) ' move to beginning

    Debug.Print newSheet.Name

End Sub

Ou si vous souhaitez que le comportement/l'interface ressemble davantage à la méthode Copy intégrée (c'est-à-dire avant/après), vous pouvez utiliser:

Function CopySheet2(ByRef sourceSheet As Worksheet, Optional ByRef beforeSheet As Worksheet, Optional ByRef afterSheet As Worksheet) As Worksheet

    Dim destinationWorkbook As Workbook
    Dim newSheet As Worksheet, lastSheet As Worksheet
    Dim lastIsVisible As Boolean

    If Not beforeSheet Is Nothing Then
        Set destinationWorkbook = beforeSheet.Parent
    ElseIf Not afterSheet Is Nothing Then
        Set destinationWorkbook = afterSheet.Parent
    Else
        Set destinationWorkbook = sourceSheet.Parent
    End If

    With destinationWorkbook
        Set lastSheet = .Worksheets(.Worksheets.Count)
    End With

    lastIsVisible = lastSheet.Visible
    lastSheet.Visible = True

    sourceSheet.Copy After:=lastSheet
    Set newSheet = lastSheet.Next

    If Not lastIsVisible Then lastSheet.Visible = False

    If Not beforeSheet Is Nothing Then
        newSheet.Move Before:=beforeSheet
    ElseIf Not afterSheet Is Nothing Then
        newSheet.Move After:=afterSheet
    Else
        newSheet.Move After:=sourceSheet
    End If

    Set CopySheet2 = newSheet

End Function
0
Tigregalis

J'ai essayé de créer une fonction "wrapper" générique fiable pour la méthode sheet.Copy à réutiliser dans plusieurs projets pendant des années.

J'ai essayé plusieurs des approches ici et je n'ai trouvé que la réponse de Mark Moore était une solution fiable dans tous les scénarios. C'est-à-dire celui qui utilise le nom "Template (2)" pour identifier la nouvelle feuille.

Dans mon cas, toute solution utilisant la "méthode ActiveSheet" était inutile car, dans certains cas, le classeur cible se trouvait dans un classeur non actif ou masqué.

De même, certains de mes cahiers ont des feuilles masquées mélangées à des feuilles visibles à divers endroits; au début, au milieu, à la fin; et donc j'ai trouvé les solutions en utilisant les options Avant: et Après: également peu fiables en fonction de l'ordre des feuilles visibles et masquées, ainsi que du facteur supplémentaire lorsque la feuille source est également masquée.

Par conséquent, après plusieurs réécritures, j'ai fini avec la fonction d'emballage suivante:

'***************************************************************************
'This is a wrapper for the worksheet.Copy method.
'
'Used to create a copy of the specified sheet, optionally set it's name, and return the new
' sheets object to the calling function.
'
'This routine is needed to predictably identify the new sheet that is added. This is because
' having Hidden sheets in a Workbook can produce unexpected results in the order of the sheets,
' eg when adding a hidden sheet after the last sheet, the new sheet doesn't always end up
' being the last sheet in the Worksheets collection.
'***************************************************************************
Function wsCopy(wsSource As Worksheet, wsAfter As Worksheet, Optional ByVal sNewSheetName As String) As Worksheet

    Dim Ws              As Worksheet

    wsSource.Copy After:=wsAfter
    Set Ws = wsAfter.Parent.Sheets(wsSource.Name & " (2)")

    'set ws Name if one supplied
    If sNewSheetName <> "" Then
        Ws.Name = sNewSheetName
    End If
    Set wsCopy = Ws
End Function

Remarque: même cette solution aura des problèmes si le nom de la feuille source est plus de 27 caractères, car le nom maximum de la feuille est 31, mais c'est généralement sous mon contrôle.

0
Rob Bishop