Erreur lors de la recherche de la dernière cellule utilisée dans Excel avec VBA


179

Lorsque je veux trouver la dernière valeur de cellule utilisée, j'utilise:

Dim LastRow As Long

LastRow = Range("E4:E48").End(xlDown).Row

Debug.Print LastRow

J'obtiens une mauvaise sortie lorsque je mets un seul élément dans une cellule. Mais lorsque je mets plus d'une valeur dans la cellule, la sortie est correcte. Quelle est la raison derrière cela?


Réponses:


309

REMARQUE : j'ai l'intention d'en faire un «article à guichet unique» où vous pouvez utiliser le Correctmoyen de trouver la dernière ligne. Cela couvrira également les meilleures pratiques à suivre lors de la recherche de la dernière ligne. Et par conséquent, je continuerai à le mettre à jour chaque fois que je rencontre un nouveau scénario / information.


Manières peu fiables de trouver la dernière ligne

Certaines des méthodes les plus courantes pour trouver la dernière ligne sont très peu fiables et ne doivent donc jamais être utilisées.

  1. Utilisé
  2. xlDown
  3. CountA

UsedRangene doit JAMAIS être utilisé pour trouver la dernière cellule contenant des données. C'est très peu fiable. Essayez cette expérience.

Tapez quelque chose dans la cellule A5. Maintenant, lorsque vous calculez la dernière ligne avec l'une des méthodes ci-dessous, cela vous donnera 5. Maintenant, colorez la cellule en A10rouge. Si vous utilisez maintenant l'un des codes ci-dessous, vous obtiendrez toujours 5. Si vous utilisez Usedrange.Rows.Countce que vous obtenez? Ce ne sera pas 5.

Voici un scénario pour montrer comment UsedRangefonctionne.

entrez la description de l'image ici

xlDown est également peu fiable.

Considérez ce code

lastrow = Range("A1").End(xlDown).Row

Que se passerait-il s'il n'y avait qu'une seule cellule ( A1) contenant des données? Vous finirez par atteindre la dernière ligne de la feuille de calcul! C'est comme sélectionner une cellule A1, puis appuyer sur la Endtouche puis sur la Down Arrowtouche. Cela vous donnera également des résultats peu fiables s'il y a des cellules vides dans une plage.

CountA est également peu fiable car il vous donnera un résultat incorrect s'il y a des cellules vides entre les deux.

Et par conséquent , il faut éviter l'utilisation de UsedRange, xlDownet CountAde trouver la dernière cellule.


Rechercher la dernière ligne dans une colonne

Pour trouver la dernière ligne de la colonne E, utilisez ceci

With Sheets("Sheet1")
    LastRow = .Range("E" & .Rows.Count).End(xlUp).Row
End With

Si vous remarquez que nous avons un .avant Rows.Count. Nous avons souvent choisi d'ignorer cela. Consultez CETTE question sur l'erreur possible que vous pourriez obtenir. Je conseille toujours d'utiliser .avant Rows.Countet Columns.Count. Cette question est un scénario classique dans lequel le code échouera car les Rows.Countretours 65536pour Excel 2003 et versions antérieures et 1048576pour Excel 2007 et versions ultérieures. De même Columns.Countrenvoie 256et 16384, respectivement.

Le fait ci-dessus qu'Excel 2007+ a des 1048576lignes met également l'accent sur le fait que nous devons toujours déclarer la variable qui contiendra la valeur de la ligne car Longau lieu de cela Integer, vous obtiendrez une Overflowerreur.

Notez que cette approche ignorera toutes les lignes masquées. En regardant ma capture d'écran ci-dessus pour la colonne A , si la ligne 8 était masquée, cette approche reviendrait à la 5place de 8.


Rechercher la dernière ligne d'une feuille

Pour trouver la Effectivedernière ligne de la feuille, utilisez ceci. Notez l'utilisation de Application.WorksheetFunction.CountA(.Cells). Cela est nécessaire car s'il n'y a pas de cellules avec des données dans la feuille de calcul .Find, vousRun Time Error 91: Object Variable or With block variable not set

With Sheets("Sheet1")
    If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
        lastrow = .Cells.Find(What:="*", _
                      After:=.Range("A1"), _
                      Lookat:=xlPart, _
                      LookIn:=xlFormulas, _
                      SearchOrder:=xlByRows, _
                      SearchDirection:=xlPrevious, _
                      MatchCase:=False).Row
    Else
        lastrow = 1
    End If
End With

Rechercher la dernière ligne d'une table (ListObject)

Les mêmes principes s'appliquent, par exemple pour obtenir la dernière ligne de la troisième colonne d'un tableau:

Sub FindLastRowInExcelTableColAandB()
Dim lastRow As Long
Dim ws As Worksheet, tbl as ListObject
Set ws = Sheets("Sheet1")  'Modify as needed
'Assuming the name of the table is "Table1", modify as needed
Set tbl = ws.ListObjects("Table1")

With tbl.ListColumns(3).Range
    lastrow = .Find(What:="*", _
                After:=.Cells(1), _
                Lookat:=xlPart, _
                LookIn:=xlFormulas, _
                SearchOrder:=xlByRows, _
                SearchDirection:=xlPrevious, _
                MatchCase:=False).Row
End With

End Sub

9
@phan: saisissez quelque chose dans la cellule A5. Maintenant, lorsque vous calculez la dernière ligne avec l'une des méthodes ci-dessus, cela vous donnera 5. Maintenant, colorez la cellule A10 en rouge. Si vous utilisez maintenant l'un des codes ci-dessus, vous obtiendrez toujours 5. Si vous utilisez Usedrange.Rows.Countce que vous obtenez? Ce ne sera pas 5. Usedrange est très peu fiable pour trouver la dernière ligne.
Siddharth Rout

6
Notez que .Find perturbe malheureusement les paramètres de l'utilisateur dans la boîte de dialogue Rechercher - c'est-à-dire qu'Excel n'a qu'un seul jeu de paramètres pour la boîte de dialogue et que vous utilisez .Find les remplace. Une autre astuce consiste à toujours utiliser UsedRange, mais à l'utiliser comme un maximum absolu (mais non fiable) à partir duquel vous déterminez le maximum correct.
Carl Colijn

4
@CarlColijn: Je n'appellerais pas ça du désordre. :) Excel simplement remembersle dernier paramètre. Même lorsque vous faites manuellement un Find, il se souvient du dernier réglage qui en fait est une aubaine si l'on connaît ce "fait"
Siddharth Rout

3
@KeithPark: Allez-y :) La connaissance n'a de sens que si elle se propage :)
Siddharth Rout

9
Je pense que votre description de UsedRange( il est très peu fiable de trouver la dernière cellule contenant des données ) est trompeuse. UsedRangen'est tout simplement pas destiné à cette fin, même si dans certains cas, il peut donner le résultat correct. Je pense que l'expérience proposée ajoute à la confusion. Le résultat obtenu avec UsedRange($ A $ 1: $ A $ 8) ne dépend pas de la première saisie des données et de leur suppression. Le chiffre de droite sera toujours le même même sans avoir saisi les données et les avoir supprimées. S'il vous plaît voir ma réponse.
sancho.s ReinstateMonicaCellio

34

Remarque: cette réponse a été motivée par ce commentaire . Le but de UsedRangeest différent de ce qui est mentionné dans la réponse ci-dessus.

Quant à la manière correcte de trouver la dernière cellule utilisée, il faut d'abord décider de ce qui est considéré comme utilisé , puis sélectionner une méthode appropriée . Je conçois au moins trois significations:

  1. Utilisé = non vide, c'est-à-dire ayant des données .

  2. Used = "... en cours d'utilisation, c'est-à-dire la section contenant des données ou une mise en forme ." Selon la documentation officielle , c'est le critère utilisé par Excel au moment de l'enregistrement. Voir aussi cette documentation officielle . Si l'on n'en est pas conscient, le critère peut produire des résultats inattendus, mais il peut aussi être intentionnellement exploité (moins souvent, sûrement), par exemple pour mettre en évidence ou imprimer des régions spécifiques, qui peuvent éventuellement ne pas avoir de données. Et, bien sûr, il est souhaitable comme critère pour la plage à utiliser lors de l'enregistrement d'un classeur, de peur de perdre une partie de son travail.

  3. Utilisé = "... en cours d'utilisation, signifiant la section qui contient des données ou une mise en forme " ou une mise en forme conditionnelle. Identique à 2., mais incluant également les cellules qui sont la cible de toute règle de mise en forme conditionnelle.

Comment trouver la dernière cellule utilisée dépend de ce que vous voulez (votre critère) .

Pour le critère 1, je suggère de lire cette réponse . Notez que cela UsedRangeest cité comme peu fiable. Je pense que c'est trompeur (c'est-à-dire "injuste" pour UsedRange), car il UsedRangen'est tout simplement pas destiné à signaler la dernière cellule contenant des données. Il ne devrait donc pas être utilisé dans ce cas, comme indiqué dans cette réponse. Voir aussi ce commentaire .

Pour le critère 2, UsedRangec'est l'option la plus fiable , par rapport aux autres options également conçues pour cet usage. Cela rend même inutile l'enregistrement d'un classeur pour s'assurer que la dernière cellule est mise à jour. Ctrl+ Endira dans une mauvaise cellule avant l'enregistrement ("La dernière cellule n'est pas réinitialisée tant que vous n'avez pas enregistré la feuille de calcul", à partir de http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10% 29.aspx . C'est une référence ancienne, mais à cet égard valable).

Pour le critère 3, je ne connais aucune méthode intégrée . Le critère 2 ne tient pas compte du formatage conditionnel. On peut avoir des cellules formatées, basées sur des formules, qui ne sont pas détectées par UsedRangeou Ctrl+ End. Dans la figure, la dernière cellule est B3, car la mise en forme lui a été appliquée explicitement. Les cellules B6: D7 ont un format dérivé d'une règle de mise en forme conditionnelle, et cela n'est pas détecté même par UsedRange. La comptabilisation de cela nécessiterait une programmation VBA.

entrez la description de l'image ici


Quant à votre question spécifique : quelle est la raison derrière cela?

Votre code utilise la première cellule de votre gamme E4: E48 comme trampoline, pour sauter avec End(xlDown).

La sortie "erronée" sera obtenue s'il n'y a pas de cellules non vides dans votre plage autre que peut-être la première. Ensuite, vous êtes bondissant dans l'obscurité , à savoir, en bas de la feuille de calcul (vous devez noter la différence entre blanc et chaîne vide !).

Notez que:

  1. Si votre plage contient des cellules non vides non contiguës, cela donnera également un résultat erroné.

  2. S'il n'y a qu'une seule cellule non vide, mais ce n'est pas la première, votre code vous donnera toujours le résultat correct.


3
Je conviens qu'il faut d'abord décider de ce qui est considéré comme utilisé . Je vois au moins 6 significations. La cellule contient: 1) des données, c'est-à-dire une formule, résultant éventuellement en une valeur vide; 2) une valeur, c'est-à-dire une formule ou une constante non vide; 3) formatage; 4) mise en forme conditionnelle; 5) une forme (y compris un commentaire) chevauchant la cellule; 6) participation à une table (objet de liste). Quelle combinaison souhaitez-vous tester? Certains (tels que les tableaux) peuvent être plus difficiles à tester et certains peuvent être rares (comme une forme en dehors de la plage de données), mais d'autres peuvent varier en fonction de la situation (par exemple, des formules avec des valeurs vides).
GlennFromIowa

20

J'ai créé cette fonction unique pour déterminer la dernière ligne, colonne et cellule, que ce soit pour les données, les cellules formatées (groupées / commentées / masquées) ou la mise en forme conditionnelle .

Sub LastCellMsg()
    Dim strResult As String
    Dim lngDataRow As Long
    Dim lngDataCol As Long
    Dim strDataCell As String
    Dim strDataFormatRow As String
    Dim lngDataFormatCol As Long
    Dim strDataFormatCell As String
    Dim oFormatCond As FormatCondition
    Dim lngTempRow As Long
    Dim lngTempCol As Long
    Dim lngCFRow As Long
    Dim lngCFCol As Long
    Dim strCFCell As String
    Dim lngOverallRow As Long
    Dim lngOverallCol As Long
    Dim strOverallCell As String

    With ActiveSheet

        If .ListObjects.Count > 0 Then
            MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
            Exit Sub
        End If

        strResult = "Workbook name: " & .Parent.Name & vbCrLf
        strResult = strResult & "Sheet name: " & .Name & vbCrLf

        'DATA:
        'last data row
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lngDataRow = .Cells.Find(What:="*", _
             After:=.Range("A1"), _
             Lookat:=xlPart, _
             LookIn:=xlFormulas, _
             SearchOrder:=xlByRows, _
             SearchDirection:=xlPrevious, _
             MatchCase:=False).Row
        Else
            lngDataRow = 1
        End If
        'strResult = strResult & "Last data row: " & lngDataRow & vbCrLf

        'last data column
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lngDataCol = .Cells.Find(What:="*", _
             After:=.Range("A1"), _
             Lookat:=xlPart, _
             LookIn:=xlFormulas, _
             SearchOrder:=xlByColumns, _
             SearchDirection:=xlPrevious, _
             MatchCase:=False).Column
        Else
            lngDataCol = 1
        End If
        'strResult = strResult & "Last data column: " & lngDataCol & vbCrLf

        'last data cell
        strDataCell = Replace(Cells(lngDataRow, lngDataCol).Address, "$", vbNullString)
        strResult = strResult & "Last data cell: " & strDataCell & vbCrLf

        'FORMATS:
        'last data/formatted/grouped/commented/hidden row
        strDataFormatRow = StrReverse(Split(StrReverse(.UsedRange.Address), "$")(0))
        'strResult = strResult & "Last data/formatted row: " & strDataFormatRow & vbCrLf

        'last data/formatted/grouped/commented/hidden column
        lngDataFormatCol = Range(StrReverse(Split(StrReverse(.UsedRange.Address), "$")(1)) & "1").Column
        'strResult = strResult & "Last data/formatted column: " & lngDataFormatCol & vbCrLf

        'last data/formatted/grouped/commented/hidden cell
        strDataFormatCell = Replace(Cells(strDataFormatRow, lngDataFormatCol).Address, "$", vbNullString)
        strResult = strResult & "Last data/formatted cell: " & strDataFormatCell & vbCrLf

        'CONDITIONAL FORMATS:
        For Each oFormatCond In .Cells.FormatConditions

            'last conditionally-formatted row
            lngTempRow = CLng(StrReverse(Split(StrReverse(oFormatCond.AppliesTo.Address), "$")(0)))
            If lngTempRow > lngCFRow Then lngCFRow = lngTempRow

            'last conditionally-formatted column
            lngTempCol = Range(StrReverse(Split(StrReverse(oFormatCond.AppliesTo.Address), "$")(1)) & "1").Column
            If lngTempCol > lngCFCol Then lngCFCol = lngTempCol
        Next
        'no results are returned for Conditional Format if there is no such
        If lngCFRow <> 0 Then
            'strResult = strResult & "Last cond-formatted row: " & lngCFRow & vbCrLf
            'strResult = strResult & "Last cond-formatted column: " & lngCFCol & vbCrLf

            'last conditionally-formatted cell
            strCFCell = Replace(Cells(lngCFRow, lngCFCol).Address, "$", vbNullString)
            strResult = strResult & "Last cond-formatted cell: " & strCFCell & vbCrLf
        End If

        'OVERALL:
        lngOverallRow = Application.WorksheetFunction.Max(lngDataRow, strDataFormatRow, lngCFRow)
        'strResult = strResult & "Last overall row: " & lngOverallRow & vbCrLf
        lngOverallCol = Application.WorksheetFunction.Max(lngDataCol, lngDataFormatCol, lngCFCol)
        'strResult = strResult & "Last overall column: " & lngOverallCol & vbCrLf
        strOverallCell = Replace(.Cells(lngOverallRow, lngOverallCol).Address, "$", vbNullString)
        strResult = strResult & "Last overall cell: " & strOverallCell & vbCrLf

        MsgBox strResult
        Debug.Print strResult

    End With

End Sub

Les résultats ressemblent à ceci:
déterminer la dernière cellule

Pour des résultats plus détaillés, certaines lignes du code peuvent être décommentées:
dernière colonne, ligne

Une limitation existe - s'il y a des tables dans la feuille, les résultats peuvent devenir peu fiables, j'ai donc décidé d'éviter d'exécuter le code dans ce cas:

If .ListObjects.Count > 0 Then
    MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
    Exit Sub
End If

2
@franklin - Je viens de remarquer un message de boîte de réception avec votre correction qui a été rejeté par les critiques. J'ai corrigé cette erreur. J'ai déjà utilisé cette fonction une fois quand j'en avais besoin et je vais l'utiliser à nouveau, alors vraiment, un grand merci, mon ami!
ZygD

11

Une note importante à garder à l'esprit lors de l'utilisation de la solution ...

LastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

... est de vous assurer que votre LastRowvariable est de Longtype:

Dim LastRow as Long

Sinon, vous obtiendrez des erreurs de SURFLUX dans certaines situations dans les classeurs .XLSX

C'est ma fonction encapsulée que j'utilise pour diverses utilisations de code.

Private Function FindLastRow(ws As Worksheet) As Long
    ' --------------------------------------------------------------------------------
    ' Find the last used Row on a Worksheet
    ' --------------------------------------------------------------------------------
    If WorksheetFunction.CountA(ws.Cells) > 0 Then
        ' Search for any entry, by searching backwards by Rows.
        FindLastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
    End If
End Function

8

J'ajouterais à la réponse donnée par Siddarth Rout pour dire que l'appel CountA peut être ignoré en demandant à Find de renvoyer un objet Range, au lieu d'un numéro de ligne, puis de tester l'objet Range renvoyé pour voir s'il s'agit de Nothing (feuille de calcul vierge) .

En outre, ma version de toute procédure LastRow renvoie un zéro pour une feuille de calcul vierge, puis je peux savoir qu'elle est vide.


8

Je me demande que personne n'ait mentionné cela, mais le moyen le plus simple d'obtenir la dernière cellule utilisée est:

Function GetLastCell(sh as Worksheet) As Range
    GetLastCell = sh.Cells(1,1).SpecialCells(xlLastCell)
End Function

Cela renvoie essentiellement la même cellule que celle obtenue par Ctrl+ Endaprès avoir sélectionné Cellule A1.

Un mot d'avertissement: Excel garde la trace de la cellule la plus en bas à droite jamais utilisée dans une feuille de calcul. Donc si, par exemple, vous entrez quelque chose dans B3 et autre chose dans H8 , puis supprimez plus tard le contenu de H8 , appuyer sur Ctrl+ vous Endamènera toujours à la cellule H8 . La fonction ci-dessus aura le même comportement.


2
Last Celldans Excel fait parfois référence à une cellule vide (de Used Range) différente de Last Used Cell;).
shA.t

1
L'OP n'avait besoin que de la dernière ligne mais vous avez raison, la dernière cellule doit être H5 ; Mais vous pouvez tester votre fonction après avoir supprimé la valeur dans A5 Vous verrez que la dernière cellule est cette cellule vide, et je pense que votre code a besoin de quelques modifications comme celle-ci Cells(1,1).Select()n'est pas valide, peut-être ActiveSheet.Cells(1,1).Select; Aussi dans VBA, il n'est pas recommandé d'utiliser Select;).
shA.t

5
Cela enfreint deux règles cardinales pour Excel VBA: N'utilisez pas Select! Et ne supposez pas que la feuille que vous voulez est active.
Rachel Hettinger

1
C'est une vieille réponse, mais il manque un Set.
BigBen

8

Étant donné que la question d'origine concerne les problèmes de recherche de la dernière cellule, dans cette réponse, je vais énumérer les différentes façons dont vous pouvez obtenir des résultats inattendus ; voir ma réponse à "Comment puis-je trouver la dernière ligne contenant des données dans la feuille Excel avec une macro?" pour mon avis sur la résolution de ce problème.

Je vais commencer par développer la réponse de sancho.s et le commentaire de GlennFromIowa , en ajoutant encore plus de détails:

[...] il faut d'abord décider de ce qui est considéré comme utilisé. Je vois au moins 6 significations. La cellule a:

  • 1) des données, c'est-à-dire une formule, aboutissant éventuellement à une valeur vide;
  • 2) une valeur, c'est-à-dire une formule ou une constante non vide;
  • 3) formatage;
  • 4) mise en forme conditionnelle;
  • 5) une forme (y compris un commentaire) chevauchant la cellule;
  • 6) participation à une table (objet de liste).

Quelle combinaison souhaitez-vous tester? Certains (comme les tableaux) peuvent être plus difficiles à tester, et certains peuvent être rares (comme une forme en dehors de la plage de données), mais d'autres peuvent varier en fonction de la situation (par exemple, des formules avec des valeurs vides).

Autres éléments à prendre en compte:

  • A) Peut-il y avoir des lignes cachées (par exemple, filtre automatique), des cellules vides ou des lignes vides?
  • B) Quel type de performance est acceptable?
  • C) La macro VBA peut-elle affecter le classeur ou les paramètres de l'application de quelque manière que ce soit?

Dans cet esprit, voyons comment les méthodes courantes d'obtention de la "dernière cellule" peuvent produire des résultats inattendus:

  • Le .End(xlDown)code de la question se cassera le plus facilement (par exemple avec une seule cellule non vide ou lorsqu'il y a des cellules vides entre les deux ) pour les raisons expliquées dans la réponse de Siddharth Rout ici (recherchez "xlDown est également peu fiable." ) 👎
  • Toute solution basée sur Counting ( CountAou Cells*.Count) ou .CurrentRegionse cassera également en présence de cellules ou de lignes vides 👎
  • Une solution consistant .End(xlUp)à rechercher en arrière à partir de la fin d'une colonne recherchera, tout comme CTRL + UP, des données (les formules produisant une valeur vide sont considérées comme des «données») dans les lignes visibles (donc l'utiliser avec le filtre automatique activé peut produire des résultats incorrects ⚠️ ).

    Vous devez faire attention pour éviter les pièges standard (pour plus de détails, je vais à nouveau consulter la réponse de Siddharth Rout ici, recherchez la section "Rechercher la dernière ligne dans une colonne" ), comme le codage en dur de la dernière ligne ( Range("A65536").End(xlUp)) au lieu de compter sur sht.Rows.Count.

  • .SpecialCells(xlLastCell)équivaut à CTRL + FIN, renvoyant la cellule la plus basse et la plus à droite de la "plage utilisée", donc toutes les mises en garde qui s'appliquent au fait de s'appuyer sur la "plage utilisée" s'appliquent également à cette méthode. De plus, la «plage utilisée» n'est réinitialisée que lors de l'enregistrement du classeur et lors de l'accès worksheet.UsedRange, elle xlLastCellpeut donc produire des résultats périmés⚠️ avec des modifications non enregistrées (par exemple après la suppression de certaines lignes). Voir la réponse à proximité par dotNET .
  • sht.UsedRange(décrit en détail dans la réponse de sancho.s ici) considère à la fois les données et le formatage (mais pas le formatage conditionnel) et réinitialise la "plage utilisée" de la feuille de calcul , qui peut ou non être ce que vous voulez.

    Notez qu'une erreur courante est d'utiliser .UsedRange.Rows.Count⚠️, qui renvoie le nombre de lignes dans la plage utilisée, pas le numéro de la dernière ligne (elles seront différentes si les premières lignes sont vides), pour plus de détails, voir la réponse de newguy à Comment puis-je trouver dernière ligne qui contient des données dans la feuille Excel avec une macro?

  • .Findvous permet de trouver la dernière ligne avec toutes les données (y compris les formules) ou une valeur non vide dans n'importe quelle colonne . Vous pouvez choisir si vous êtes intéressé par des formules ou des valeurs, mais le hic, c'est que cela réinitialise les valeurs par défaut dans la boîte de dialogue Rechercher d'Excel ️️⚠️, ce qui peut être très déroutant pour vos utilisateurs. Il doit également être utilisé avec précaution, voir la réponse de Siddharth Rout ici (section "Trouver la dernière ligne d'une feuille" )
  • Les solutions plus explicites qui vérifient l'individu Cellsdans une boucle sont généralement plus lentes que la réutilisation d'une fonction Excel (bien qu'elles puissent toujours être performantes), mais vous permettent de spécifier exactement ce que vous voulez trouver. Voir ma solution basée sur UsedRangeet les tableaux VBA pour trouver la dernière cellule avec des données dans la colonne donnée - il gère les lignes cachées, les filtres, les blancs, ne modifie pas les valeurs par défaut de recherche et est assez performant.

Quelle que soit la solution que vous choisissez, soyez prudent

  • à utiliser à la Longplace de Integerpour stocker les numéros de ligne (pour éviter d'avoir Overflowplus de 65k lignes) et
  • pour toujours spécifier la feuille de calcul avec laquelle vous travaillez (c'est- Dim ws As Worksheet ... ws.Range(...)à- dire au lieu de Range(...))
  • lorsque vous utilisez .Value(qui est a Variant), évitez les transtypages implicites, .Value <> ""car ils échoueront si la cellule contient une valeur d'erreur.

4

Cependant, cette question cherche à trouver la dernière ligne en utilisant VBA, je pense qu'il serait bon d'inclure une formule matricielle pour la fonction de feuille de calcul car elle est visitée fréquemment:

{=ADDRESS(MATCH(INDEX(D:D,MAX(IF(D:D<>"",ROW(D:D)-ROW(D1)+1)),1),D:D,0),COLUMN(D:D))}

Vous devez entrer la formule sans crochets, puis appuyez sur Shift+ Ctrl+ Enterpour en faire une formule matricielle.

Cela vous donnera l'adresse de la dernière cellule utilisée dans la colonne D.


1
J'aime ça. Je pourrais modifier légèrement pour obtenir uniquement le numéro de ligne ... '{= MATCH (INDEX (D: D, MAX (IF (D: D <> "", ROW (D: D) -ROW (D1) +1)) , 1), D: D, 0)} '
PGSystemTester

3
sub last_filled_cell()
msgbox range("A65536").end(xlup).row
end sub

Voici A65536la dernière cellule de la colonne A ce code a été testé sur Excel 2003.


Pouvez-vous expliquer comment votre code répond à cette vieille question?
shoover

1
Bien que cette réponse soit probablement correcte et utile, il est préférable que vous y incluiez des explications pour expliquer comment elle aide à résoudre le problème. Cela devient particulièrement utile à l'avenir, s'il y a un changement (peut-être sans rapport) qui l'empêche de fonctionner et que les utilisateurs doivent comprendre comment cela fonctionnait autrefois.
Kevin Brown

2

Je cherchais un moyen d'imiter le CTRL+ Shift+ End, donc la solution dotNET est excellente, sauf avec mon Excel 2010, je dois ajouter un setsi je veux éviter une erreur:

Function GetLastCell(sh As Worksheet) As Range
  Set GetLastCell = sh.Cells(1, 1).SpecialCells(xlLastCell)
End Function

et comment le vérifier par vous-même:

Sub test()
  Dim ws As Worksheet, r As Range
  Set ws = ActiveWorkbook.Sheets("Sheet1")
  Set r = GetLastCell(ws)
  MsgBox r.Column & "-" & r.Row
End Sub

1

Voici mes deux cents.

À mon humble avis, le risque d'une ligne cachée avec des données exclues est trop important pour xlUpêtre considéré comme une réponse unique . Je conviens que c'est simple et fonctionnera la plupart du temps, mais cela présente le risque de sous-estimer la dernière ligne, sans aucun avertissement. Cela pourrait produire CATASTROPHIQUE résultats à un moment donné pour quelqu'un qui a sauté sur Stack Overlow et cherchait un "moyen sûr" de capturer cette valeur.

La Findméthode est sans faille et je l'approuverais comme une réponse unique . Cependant, l'inconvénient de changer leFind paramètres peut être gênant, en particulier si cela fait partie d'un UDF.

Les autres réponses affichées sont correctes, mais la complexité devient un peu excessive. Voici donc ma tentative de trouver un équilibre entre fiabilité, complexité minimale et non utilisation Find.

Function LastRowNumber(Optional rng As Range) As Long

If rng Is Nothing Then
    Set rng = ActiveSheet.UsedRange
Else
    Set rng = Intersect(rng.Parent.UsedRange, rng.EntireColumn)
    If rng Is Nothing Then
        LastRowNumber = 1
        Exit Function
    ElseIf isE = 0 Then
        LastRowNumber = 1
        Exit Function

    End If

End If

LastRowNumber = rng.Cells(rng.Rows.Count, 1).Row

Do While IsEmpty(Intersect(rng, _
    rng.Parent.Rows(LastRowNumber)))

    LastRowNumber = LastRowNumber - 1
Loop

End Function

Pourquoi c'est bon:

  • Raisonnablement simple, pas beaucoup de variables.
  • Permet plusieurs colonnes.
  • Ne modifie pas les Findparamètres
  • Dynamique si utilisé comme UDF avec la colonne entière sélectionnée.

Pourquoi c'est mauvais:

  • Avec de très grands ensembles de données et un écart massif entre la plage utilisée et la dernière ligne dans les colonnes spécifiées, cela fonctionnera plus lentement, dans de rares cas, SIGNIFICATIVEMENT plus lent.

Cependant, je pense qu'une solution unique qui présente l'inconvénient de gâcher les findparamètres ou de fonctionner plus lentement est une meilleure solution globale. Un utilisateur peut ensuite bricoler ses paramètres pour essayer de s'améliorer, sachant ce qui se passe avec son code. L'utilisation xLUpn'avertira pas des risques potentiels et ils pourraient continuer pendant qui sait combien de temps ne sachant pas que leur code ne fonctionnait pas correctement.


1

Au cours des 3 dernières années, ce sont les fonctions que j'utilise pour trouver la dernière ligne et la dernière colonne par colonne définie (pour ligne) et ligne (pour colonne):

Dernière colonne:

Function lastCol(Optional wsName As String, Optional rowToCheck As Long = 1) As Long

    Dim ws  As Worksheet

    If wsName = vbNullString Then
        Set ws = ActiveSheet
    Else
        Set ws = Worksheets(wsName)
    End If

    lastCol = ws.Cells(rowToCheck, ws.Columns.Count).End(xlToLeft).Column

End Function

Dernière rangée:

Function lastRow(Optional wsName As String, Optional columnToCheck As Long = 1) As Long

    Dim ws As Worksheet

    If wsName = vbNullString Then
        Set ws = ActiveSheet
    Else
        Set ws = Worksheets(wsName)
    End If

    lastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row

End Function

Pour le cas de l'OP, voici la façon d'obtenir la dernière ligne de la colonne E:

Debug.Print lastRow(columnToCheck:=Range("E4:E48").Column)

Dernière ligne, en comptant les lignes vides avec des données:

Ici, nous pouvons utiliser les formules Excel bien connues , qui nous donnent la dernière ligne d'une feuille de calcul dans Excel, sans impliquer VBA -=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

Afin de mettre cela dans VBA et de ne rien écrire dans Excel, en utilisant les paramètres de ces dernières fonctions, quelque chose comme ça pourrait être à l'esprit:

Public Function LastRowWithHidden(Optional wsName As String, Optional columnToCheck As Long = 1) As Long

    Dim ws As Worksheet

    If wsName = vbNullString Then
        Set ws = ActiveSheet
    Else
        Set ws = Worksheets(wsName)
    End If

    Dim letters As String
    letters = ColLettersGenerator(columnToCheck)
    LastRowWithHidden = ws.Evaluate("=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(" & letters & "))),ROW(" & letters & " )),0)")

End Function

Function ColLettersGenerator(col As Long) As String

    Dim result As Variant
    result = Split(Cells(1, col).Address(True, False), "$")
    ColLettersGenerator = result(0) & ":" & result(0)

End Function

Cela renverra un résultat incorrect si la dernière ligne / colonne est masquée.
PGSystemTester

@PGSystemTester - oui, mais d'après ce que je comprends, quand je le programme, s'il est caché, ce n'est pas la dernière colonne / ligne qui est nécessaire.
Vityata

Heureux que cela fonctionne pour vous. Je soupçonne que votre situation n'est pas un cas d'utilisation typique. Plus fréquemment, lorsque je travaille avec des clients qui ont besoin de la dernière ligne, ils recherchent la cellule la plus basse avec des données, pas la cellule la plus basse visible avec des données. Quoi qu'il en soit ... content que ça marche. 👍
PGSystemTester

@PGSystemTester - J'ai compris votre point, mais prendre soin de la structure et ne pas autoriser les cellules invisibles fonctionne comme un charme.
Vityata

@PGSystemTester - oui, si la tâche autorise éventuellement des lignes vides, j'utiliserais probablement EVAL()la fameuse formule Excel. Bien que les gens puissent penser que Eval()c'est mal et que c'est une autre histoire intéressante sur laquelle écrire ...
Vityata

0
Sub lastRow()

    Dim i As Long
        i = Cells(Rows.Count, 1).End(xlUp).Row
            MsgBox i

End Sub

sub LastRow()

'Paste & for better understanding of the working use F8 Key to run the code .

dim WS as worksheet
dim i as long

set ws = thisworkbook("SheetName")

ws.activate

ws.range("a1").select

ws.range("a1048576").select

activecell.end(xlup).select

i= activecell.row

msgbox "My Last Row Is " & i

End sub
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.