【问题标题】:Copy unknown range from specific worksheet从特定工作表复制未知范围
【发布时间】:2018-09-28 13:32:37
【问题描述】:

我正在尝试在 VBA 中进行复制,作为更大宏的一部分,因此它需要在 VBA 中,特定工作表中的未知范围。

如果我在那个工作表中,我有这个代码可以工作:

Sub Copy()
Range("O2", Range("O" & Cells(Rows.Count, "A").End(xlUp).Row)).copy
End Sub()

我有以下适用于特定范围的内容:

Sub Test()
    Worksheets("Data").Range("O2:O10").Copy
End Sub()

如何使第二个代码不具体。

谢谢,

【问题讨论】:

  • “未知”是指“变量”吗?
  • 是的,或者每次运行宏的行数都不一样

标签: vba excel copy


【解决方案1】:

您应该练习始终完全限定您的所有SheetRange 对象。

下面的代码有点长,但最好定义和设置所有对象和变量。

代码

Option Explicit

Sub Test()

Dim Sht As Worksheet
Dim LastRow As Long

' set your worksheet object
Set Sht = ThisWorkbook.Worksheets("Data")

With Sht
    ' get last row in column A
    LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row

    ' copy dynamic range in column O
    .Range("O2:O" & LastRow).Copy
End With

End Sub

【讨论】:

  • 延迟绑定在这里不是更好的选择吗?我怀疑这对内存的影响相对较小,但有助于促进更好的编码实践
  • @Zac 你想在这里后期绑定什么?我的意思是,Worksheets 对象等只能在 Excel 环境中工作。没有 Excel,就没有使用它们的意义……
  • 如果我必须再次使用它,我不会使用With ThisWorkbook.Worksheets("Data")。在这种情况下,我会设置一个对象并使用它。但如果它只使用过一次,那将是我的偏好
  • @Vityata:完全同意,但我正在修复某人的代码中的问题,他们像这样打开了数百个参考,并且一直在工作表中出现问题。我确实同意.Net 更花哨:)
  • @Zac - 修复某人的 VBA 代码中的问题可能是最糟糕的任务。尽量不要太讨厌前一个人,这会有所帮助:D
【解决方案2】:

最简单和最肮脏的解决方案是这个:

Range("O2:O" & Cells(Rows.Count, "A").End(xlUp).Row).Copy

或者您可以将最后一行隔离为单独的变量:

lastRow = Cells(Rows.Count, "A").End(xlUp).Row
Range("O2:O" & lastRow).Copy

最后,可以决定将要复制的范围声明为单独的变量并使用它,同时声明父工作表:

Public Sub TestMe()

    Dim lastRow As Long
    Dim ws As Worksheet
    Dim rangeToCopy As Range

    Set ws = workshetes("Sheet1")

    With ws
        lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
        Set rangeToCopy = .Range("O2:O" & lastRow)
        rangeToCopy.Copy
    End With

End Sub

更进一步的是使用专用函数来查找每个工作表的最后一行 (GitHub repo here):

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

    Dim ws As Worksheet
    Set ws = Worksheets(wsName)

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

End Function

【讨论】:

  • 对于 github 位,如果wsName 与书中的工作表名称不匹配,那不会崩溃吗?
  • @Marcucciboy2 - wsName 是可选参数。如果未提供,则返回ActiveSheet 的最后一行。
  • 我的意思是,例如,如果用户在规范中拼写错误的工作表名称,它就会出错
  • @Marcucciboy2 - 是的。但它是开发人员,而不是用户,它不打算成为 UDF。但是,这是完全可以预料的行为。如果开发人员在Set ws = workshetes("Sheet1") 上拼错“Sheet1”,也会出现同样的错误。
  • @Vityata 那么它不应该是明确的Public,命名为GetLastRow,在指定Option Private Module 的模块中吗? ;-) ..FWIW 我同意让函数抛出,但我会让它抛出一个自定义消息,即明确处理 RTE 9 并用Specified worksheet could not be found 或其他东西重新提升它。还要处理错误 91 并使用 There is currently no active worksheet 重新引发它,..并采用可选的 Workbook 参数,这样您就不需要 .Activate 工作簿来使用该功能!
【解决方案3】:

在某些时候,您的代码必须知道要复制的范围,对吗?您将其分配给一个变量并使用它。

Option Explicit 

Sub Test()
  Dim startRow as Long
  startRow = 'your method of determining the starting row
  Dim startCol as Long
  startCol = 'your method of determining the starting column
  Dim endRow as Long
  endRow = 'your method of determining the ending row (Last used row would work just fine)
  Dim endCol as Long
  endCol = 'your method of determining the ending column

  With Worksheets("Data")
    .Range(.Cells(startRow, startCol), .Cells(endRow, endCol)).Copy
  End with

End Sub

【讨论】:

  • 在其中添加一些Option Explicit,这是很好的衡量标准。如果一个人拿起它,你就让世界变得更美好。 :)
【解决方案4】:

您可以使用函数将“种子”范围传递给并返回从传递的一个到同一列中最后一个非空单元格的范围,如下所示(cmets 中的解释)

Function GetRange(rng As Range) As Range
    With rng.Parent ' reference passed range parent worksheet
        Set GetRange = .Range(rng, .Cells(.Rows.Count, rng.Column).End(xlUp)) ' return referenced sheet range from passed range to passed range column last not empty cell
    End With
End Function

按如下方式使用:

Sub Test()
    GetRange(Worksheets("Data").Range("O2")).Copy
End Sub

您可以增强该功能并让它处理给定的“最终”行

Function GetRange(rng As Range, Optional finalRow As Variant) As Range
    With rng.Parent ' reference passed range parent worksheet
        If IsMissing(finalRow) Then ' if no "final" row passed
            Set GetRange = .Range(rng, .Cells(.Rows.Count, rng.Column).End(xlUp)) ' return referenced sheet range from passed range to passed range column last not empty cell
        Else 'else
            Set GetRange = .Range(rng, .Cells(finalRow, rng.Column)) ' return referenced sheet range from passed range to passed range column cell in give "final" row
        End If
    End With

按如下方式使用:

Sub Test()
    GetRange(Worksheets("Data").Range("O2"), 2).Copy
End Sub

将“final”行保留为可选,该函数可以在有或没有传递的情况下使用:

Sub Test()
    GetRange(Worksheets("Data").Range("O2")).Copy ' this will copy worksheet "Data" range from row 2 down to its column "O" last not empty row
    GetRange(Worksheets("Data").Range("O2"), 3).Copy ' this will copy worksheet "Data" range from row 2 down to row 3
End Sub

【讨论】:

    【解决方案5】:

    你显然不喜欢使用变量,所以:

    Worksheets("Data").Range("O2", Worksheets("Data").Range("O" & Cells(Rows.Count, "A").End(xlUp).Row)).copy
    

    足够了。

    一般来说,更常见的解决方案是使用 intersect 和 CurrentRegion:

    Application.intersect(Worksheets("Data").Range("O2").CurrentRegion,Worksheets("Data").Range("O2:O999999")).copy
    

    【讨论】:

    • 好吧..我倾向于使用变量,但在这种情况下,我找不到任何有效的示例..所以最接近的是没有它们的示例..
    • @akire 很公平 :) 话虽如此,Application.Intersect 1 衬里在中间 VBA 代码中相当普遍,所以我不能否认缺少变量通常是一种奖励。这实际上取决于您正在编写的代码 imo。我见过很多过度使用变量的代码...
    • 是的,我也不会说我擅长编写代码,仍然是一个喜欢学习的初学者:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多