繁体   English   中英

在 Excel VBA 中查找最后使用的单元格

[英]Find last used cell in Excel VBA

当我想找到最后使用的单元格值时,我使用:

Dim LastRow As Long

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

Debug.Print LastRow

当我将单个元素放入单元格时,我得到了错误的 output。 但是当我在单元格中输入多个值时,output 是正确的。 这背后的原因是什么?

注意:我打算将其设为“一站式帖子”,您可以在其中使用Correct的方式找到最后一行。 这还将涵盖查找最后一行时要遵循的最佳实践。 因此,每当我遇到新的场景/信息时,我都会继续更新它。


找到最后一行的不可靠方法

一些最常见的查找最后一行的方法非常不可靠,因此永远不应该使用。

  1. 使用范围
  2. xl向下
  3. 计数A

UsedRange永远不应该用于查找最后一个有数据的单元格。 这是非常不可靠的。 试试这个实验。

在单元格A5中键入内容。 现在,当您使用下面给出的任何方法计算最后一行时,它会给您 5。现在将单元格A10成红色。 如果您现在使用以下任何代码,您仍然会得到 5。如果您使用Usedrange.Rows.Count ,您会得到什么? 不会是5。

这是一个展示UsedRange如何工作的场景。

在此处输入图像描述

xlDown同样不可靠。

考虑这段代码

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

如果只有一个单元格( A1 )有数据会发生什么? 您最终将到达工作表中的最后一行! 这就像选择单元格A1 ,然后按End键,然后按向下箭头键。 如果某个范围内有空白单元格,这也会给您带来不可靠的结果。

CountA也是不可靠的,因为如果中间有空白单元格,它会给你不正确的结果。

因此,应该避免使用UsedRangexlDownCountA来查找最后一个单元格。


在列中查找最后一行

要查找 Col E 中的最后一行,请使用此

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

如果您注意到我们有一个. Rows.Count之前。 我们经常选择忽略这一点。 有关您可能遇到的错误,请参阅问题。 我总是建议使用. Rows.CountColumns.Count之前。 该问题是代码将失败的经典场景,因为Rows.Count对于 Excel 2003 及更早版本返回65536 ,而对于 Excel 2007 及更高版本返回1048576 同样Columns.Count分别返回25616384

Excel 2007+ 有1048576行的上述事实也强调了这样一个事实,即我们应该始终将保存行值的变量声明为Long而不是Integer ,否则您将收到Overflow错误。

请注意,此方法将跳过任何隐藏的行。 回顾我上面的 A 列屏幕截图,如果第 8 行被隐藏,这种方法将返回5而不是8


在工作表中查找最后一行

要在工作表中找到Effective的最后一行,请使用它。 注意Application.WorksheetFunction.CountA(.Cells)的使用。 这是必需的,因为如果工作表中没有包含数据的单元格,则.Find将为您提供Run 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

查找表中的最后一行 (ListObject)

同样的原则也适用,例如获取表格第三列的最后一行:

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

注意:这个答案是由这个评论激发的。 UsedRange的目的与上面答案中提到的不同。

至于找到最后使用的单元格的正确方法,首先要确定什么是已使用的,然后选择合适的方法 我认为至少三个含义:

  1. Used = 非空白,即有数据

  2. Used = "... in use,表示包含数据或格式的部分。" 根据官方文档,这是 Excel 在保存时使用的标准。 另请参阅此官方文档 如果没有意识到这一点,该标准可能会产生意想不到的结果,但它也可能被有意利用(不太常见,当然),例如,突出或打印最终可能没有数据的特定区域。 而且,当然,最好将其作为保存工作簿时使用的范围的标准,以免丢失部分工作。

  3. Used = "... in use,表示包含数据或格式的部分"或条件格式。 与 2. 相同,但也包括作为任何条件格式规则目标的单元格。

如何找到最后使用的单元格取决于想要什么(您的标准)

对于标准 1,我建议阅读此答案 请注意, UsedRange被引用为不可靠。 我认为这具有误导性(即,对UsedRange “不公平”),因为UsedRange根本不打算报告包含数据的最后一个单元格。 因此,如该答案所示,在这种情况下不应使用它。 另请参阅此评论

对于标准 2,与同样为此用途设计的其他选项相比, UsedRange是最可靠的选项 它甚至不需要保存工作簿以确保最后一个单元格已更新。 Ctrl + End将在保存之前转到错误的单元格(“在保存工作表之前不会重置最后一个单元格”,来自http://msdn.microsoft.com/en-us/library/aa139976%28v=office。 10%29.aspx 。这是一个旧参考,但在这方面有效)。

对于标准 3,我不知道任何内置方法 标准 2 不考虑条件格式。 可能有基于公式的格式化单元格,但UsedRangeCtrl + End未检测到。 在图中,最后一个单元格是 B3,因为它已明确应用了格式。 单元格 B6:D7 具有从条件格式规则派生的格式,即使UsedRange也检测不到。 考虑到这一点需要一些 VBA 编程。

在此处输入图像描述


至于你的具体问题这背后的原因是什么?

您的代码使用 E4:E48 范围内的第一个单元格作为蹦床,用于使用End(xlDown)向下

如果您的范围内除了第一个以外没有非空白单元格,则将获得“错误”输出。 然后,您在黑暗中跳跃,即在工作表中向下跳跃(您应该注意空白字符串和空字符串之间的区别!)。

注意:

  1. 如果您的范围包含不连续的非空白单元格,那么它也会给出错误的结果。

  2. 如果只有一个非空白单元格,但它不是第一个,您的代码仍然会给您正确的结果。

我创建了这个一站式函数来确定最后一行、最后一列和最后一个单元格,无论是数据、格式化(分组/注释/隐藏)单元格还是条件格式

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

结果如下所示:
确定最后一个单元格

要获得更详细的结果,可以取消注释代码中的某些行:
最后一列,行

存在一个限制 - 如果工作表中有表格,结果可能会变得不可靠,因此我决定避免在这种情况下运行代码:

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

使用该解决方案时要记住的一个重要注意事项...

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

...是为了确保您的LastRow变量是Long类型:

Dim LastRow as Long

否则,在 .XLSX 工作簿中的某些情况下,您最终会遇到 OVERFLOW 错误

这是我在各种代码使用中的封装函数。

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

由于最初的问题是关于找到最后一个单元格的问题,因此在这个答案中,我将列出可以获得意外结果的各种方法 请参阅我对“如何使用宏在 Excel 工作表中找到包含数据的最后一行?”的回答。 我对解决这个问题的看法。

我将首先扩展sancho.s 的答案和 GlennFromIowa 的评论,添加更多细节:

[...] 首先要决定什么被认为是使用的。 我看到至少6个意思。 细胞有:

  • 1) 数据,即公式,可能会产生空值;
  • 2)一个值,即一个非空白公式或常数;
  • 3) 格式化;
  • 4) 条件格式;
  • 5)与单元格重叠的形状(包括评​​论);
  • 6)参与一个表(列表对象)。

您要测试哪种组合? 有些(例如表格)可能更难测试,有些可能很少见(例如超出数据范围的形状),但其他可能会因情况而异(例如,具有空白值的公式)。

您可能需要考虑的其他事项:

  • A) 可以有隐藏行(例如自动过滤器)、空白单元格或空白行吗?
  • B) 什么样的表现是可以接受的?
  • C) VBA 宏能否以任何方式影响工作簿或应用程序设置?

考虑到这一点,让我们看看获取“最后一个单元格”的常用方法如何产生意想不到的结果:

  • 问题中的.End(xlDown)代码最容易中断(例如,使用单个非空单元格或中间有空白单元格时),原因在此处Siddharth Rout 的回答中解释了原因(搜索“xlDown is equals不可靠。” )👎
  • 任何基于Count ing( CountACells*.Count )或.CurrentRegion的解决方案也会在出现空白单元格或行时中断👎
  • 涉及.End(xlUp)从列末尾向后搜索的解决方案将与 CTRL+UP 一样,在可见行中查找数据(产生空白值的公式被视为“数据”)(因此在启用自动过滤器的情况下使用它)可能会产生不正确的结果⚠️)。

    您必须注意避免标准陷阱(有关详细信息,我将在此处再次参考Siddharth Rout 的答案,查找“查找列中的最后一行”部分),例如硬编码最后一行( Range("A65536").End(xlUp) ) 而不是依赖sht.Rows.Count

  • .SpecialCells(xlLastCell)等价于 CTRL+END,返回“已使用范围”的最底部和最右侧单元格,因此适用于依赖“已使用范围”的所有注意事项也适用于此方法。 此外,“使用范围”仅在保存工作簿和访问worksheet.UsedRange时重置,因此xlLastCell可能会产生陈旧的结果⚠️ 未保存的修改(例如,在删除某些行之后)。 请参阅dotNET 附近的答案
  • sht.UsedRange (在sancho.s 的答案中详细描述)考虑数据和格式(尽管不是条件格式)并重置工作表的“使用范围” ,这可能是您想要的,也可能不是您想要的。

    注意一个常见的错误️是使用.UsedRange.Rows.Count ⚠️,它返回的是已用范围内的数,而不是最后的行数(如果前几行为空白,它们会有所不同),详情见newguy的回答如何使用宏在 Excel 工作表中找到包含数据的最后一行?

  • .Find允许您在任何列中查找包含任何数据(包括公式)或非空白值的最后一行。 您可以选择是否对公式或值感兴趣,但问题是它会重置 Excel 查找对话框中的默认值️️⚠️,这可能会让您的用户非常困惑。 它还需要小心使用,请参阅Siddharth Rout 的答案here( “Find Last Row in a Sheet”部分)
  • 在循环中检查单个Cells ' 的更明确的解决方案通常比重新使用 Excel 函数慢(尽管仍然可以是高性能的),但让您准确指定您想要查找的内容。 请参阅基于UsedRange和 VBA 数组的解决方案,以查找给定列中包含数据的最后一个单元格——它处理隐藏行、过滤器、空白,不修改 Find 默认值并且非常高效。

无论您选择哪种解决方案,都要小心

  • 使用Long而不是Integer来存储行号(以避免出现超过 65k 行的Overflow )和
  • 始终指定您正在使用的工作表(即Dim ws As Worksheet ... ws.Range(...)而不是Range(...)
  • 使用.Value (这是一个Variant )时避免像.Value <> ""这样的隐式强制转换,因为如果单元格包含错误值,它们将失败。

我要补充 Siddarth Rout 给出的答案,说可以通过让 Find 返回 Range 对象而不是行号来跳过 CountA 调用,然后测试返回的 Range 对象以查看它是否为 Nothing(空白工作表) .

另外,我会让我的任何 LastRow 过程版本为空白工作表返回零,然后我可以知道它是空白的。

我想知道没有人提到这一点,但获取最后使用的单元格的最简单方法是:

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

这实质上返回的单元格与您在选择 Cell A1后通过Ctrl + End获得的单元格相同。

提醒一句:Excel 会跟踪曾经在工作表中使用过的最右下角的单元格。 因此,例如,如果您在B3中输入内容,在H8中输入其他内容,然后删除H8的内容,则按Ctrl + End仍会将您带到H8单元格。 上述函数将具有相同的行为。

于 2021 年底更新

有了 Excel 的新计算引擎和数组功能,以及过滤器功能,我相信这个话题现在应该少很多争议,并且以下选项提供了速度、可靠性和简单性的最佳组合(过去已证明难以平衡,因为这里的众多帖子说明了)。

此外,我将last used定义为不是isBlank函数定义的空白。

Excel公式

首先,请注意,过滤器函数使得使用以下公式获取最后一个单元格变得更加简单,用于特定的行或列(在这些情况下为Column ARow 1 ):

=MAX(FILTER(ROW(A:A),NOT(ISBLANK(A:A))))
=MAX(FILTER(COLUMN(1:1),NOT(ISBLANK(1:1))))

最后一行特定范围的 VBA 函数

使用上述函数,我们可以将其转换为 VBA 函数,但通过限制范围使其更快,同时通过执行多个列来扩展其功能(感谢Chris Neilsen的即时反馈 tweeking/建议)。 我还发现,通过将每一列限定为仅比前一行高一行的范围,我发现了速度的巨大提升。

Function FindLastRowInRange(someColumns As Range) As Long
Const zFx = "=MAX(FILTER(ROW(????),NOT(ISBLANK(????)),0))"
   
   Dim tRng As Range, i As Long, tRow As Long, pRng As Range
   With someColumns.Worksheet
      Set tRng = Intersect(someColumns.EntireColumn, .UsedRange)
      
      For i = 1 To tRng.Columns.Count
         
         Set pRng = Intersect(tRng.Columns(i), _
         Range(.Rows(FindLastRowInRange + 1), .Rows(.Rows.Count)))
         
         If Not pRng Is Nothing Then
            tRow = .Evaluate(Replace(zFx, "????", _
               pRng.Address, 1, -1))
         
            If tRow > FindLastRowInRange Then _
               FindLastRowInRange = tRow
            
         End If
      Next i
   End With
End Function

工作表中最后一行的 VBA 函数

要考虑整个工作表(所有列),我建议使用引用前一个的不同 VBA 公式,但它是一个Volatile Function 这可确保公式随着工作表的任何更改而更新。 显然,可以将这两个公式结合起来,但我更喜欢限制 volatile 函数的使用。

Function FindLastRowInSheet(anywhereInSheet As Range) As Long
      Application.Volatile
      FindLastRowInSheet = FindLastRowInRange(anywhereInSheet.Worksheet.UsedRange)
End Function

与其他选项相比的优势

  • 允许工作表中的某些或所有行/列而不改变方法。
  • 不可能像xlup一样丢失隐藏行
  • 忽略格式化/使用范围问题。
  • 不干扰用户的Find设置。
  • 使用比 VBA 计算更快的工作表功能。
  • 没有计数细胞(性能猪)。

希望这能结束辩论,但如果有人发现这方面的弱点,请分享。

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

在这里, A65536是 A 列中的最后一个单元格,此代码在 excel 2003 上进行了测试。

然而,这个问题正在寻求使用 VBA 找到最后一行,我认为最好为工作表函数包含一个数组公式,因为它经常被访问:

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

您需要输入不带括号的公式,然后按Shift + Ctrl + Enter使其成为数组公式。

这将为您提供 D 列中最后使用的单元格的地址。


感谢pgsystemtester ,这将为您提供最后使用的单元格的行号:

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

我正在寻找一种模仿CTRL + Shift + End的方法,所以 dotNET 解决方案很棒,除了我的 Excel 2010,如果我想避免错误,我需要添加一个set

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

以及如何自己检查:

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
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

在过去 3 年多的时间里,这些是我用来查找每个定义的列(对于行)和行(对于列)的最后一行和最后一列的函数:

最后一栏:

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

最后一行:

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

对于 OP,这是获取E列最后一行的方法:

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

最后一行,用数据计算空行:

在这里我们可以使用众所周知的 Excel 公式,它为我们提供 Excel 中工作表的最后一行,而不涉及 VBA - =IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

为了把它放在 VBA 中而不是在 Excel 中写任何东西,使用后面函数的参数,可以考虑这样的事情:

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

当我想找到最后使用的单元格值时,我使用:

Dim LastRow As Long

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

Debug.Print LastRow

当我将单个元素放入单元格时,我得到了错误的输出。 但是当我在单元格中放入多个值时,输出是正确的。 这背后的原因是什么?

常规范围或表中的最后一行 (ListObject)

  1. 如果范围是常规范围或表格(列表对象),则查找最后一行需要使用不同的方法。
  2. 查找表中的最后一行需要指定其他参数(表名,列相对于第一个表列的位置)。

无论范围类型如何,我都为最后一行创建了这个通用函数。 只要给它任何单元格引用,它就会返回最后一行。 知道范围特征没有任何麻烦,特别是如果您的范围有时是常规范围,有时是 ListObject。 在表上使用常规范围方法可能会返回错误的结果。 当然,您可以提前计划并每次都使用正确的方法,但是如果您可以使用通用功能,为什么还要麻烦呢?

 Sub RunMyLastRow() Dim Result As Long Result = MyLastRow(Worksheets(1).Range("A1")) End Sub
    Function MyLastRow(RefrenceRange As Range) As Long
    Dim WS As Worksheet
    Dim TableName As String
    Dim ColNumber As Long
    Dim LastRow As Long
    Dim FirstColumnTable As Long
    Dim ColNumberTable As Long
    Set WS = RefrenceRange.Worksheet
    TableName = GetTableName(RefrenceRange)
    ColNumber = RefrenceRange.Column
    
    ''If the table (ListObject) does not start in column "A" we need to calculate the 
    ''first Column table and how many Columns from its beginning the Column is located.
    If TableName <> vbNullString Then
     FirstColumnTable = WS.ListObjects(TableName).ListColumns(1).Range.Column
     ColNumberTable = ColNumber - FirstColumnTable + 1
    End If 

    If TableName = vbNullString Then
    LastRow = WS.Cells(WS.Rows.Count, ColNumber).End(xlUp).Row
    Else
    LastRow = WS.ListObjects(TableName).ListColumns(ColNumberTable).Range.Find( _
               What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
    End If
    MyLastRow = LastRow
    End Function
    
 ''Get Table Name by Cell Range Function GetTableName(RefrenceRange As Range) As String If RefrenceRange.ListObject Is Nothing Then GetTableName = vbNullString Else GetTableName = RefrenceRange.ListObject.Name End If End Function

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM