【问题标题】:Why does an Excel VBA routine not work if it is called by another routine?如果 Excel VBA 例程被另一个例程调用,为什么它不起作用?
【发布时间】:2015-08-24 17:09:17
【问题描述】:

我有一个子,我调用它作为另一个子的一部分。当它自己运行时,它工作正常,但是当它从另一个 sub 调用时,它并不能完全正确地工作。主子保存默认值然后:

Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False

Call FillInCoding

这是不工作的子,我认为可能是这种编码是复制和粘贴特殊:

Sub FillInCoding()

'clears the area to be pasted into
Worksheets(1).Range("A11:G98567").ClearContents


Dim JE As Worksheet
Dim Sheet1 As Worksheet
Dim lastRow As Long

Set JE = ThisWorkbook.Worksheets(1)
Set Sheet1 = ThisWorkbook.Worksheets(2)


lastRow = Sheet1.Range("R65536").End(xlUp).Row

'This part is copying the values of the ranges from worksheet #2 into the worksheet #1 destination
 Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow).PasteSpecial xlPasteValues
 Worksheets(2).Range("AN2:AN" & lastRow).Copy
 Worksheets(1).Range("B11:B" & lastRow).PasteSpecial xlPasteValues
 Worksheets(2).Range("R2:R" & lastRow).Copy
 Worksheets(1).Range("E11:E" & lastRow).PasteSpecial xlPasteValues
 Worksheets(2).Range("AO2:AO" & lastRow).Copy
 Worksheets(1).Range("G11:G" & lastRow).PasteSpecial xlPasteValues


End Sub

我尝试设置范围,但它粘贴的是单元格公式而不是值,所以这是我可以让它做我需要做的事情的唯一简单方法。归根结底,我需要将您在工作表 2 上看到的不同范围内的内容(每次运行时可能是不同的长度)作为值复制到工作表 1 中。

如果有人知道如何在最后一行发生变化时轻松地将不同工作表上的范围设置为等于不同的值,并且它们位于不同工作表上的不同位置,那将是理想的。

例如在第二张纸上它可能是

A1="XYZ"
B1="100000"
C1="52.00"
D1="office supplies"

A2="YZA"
B2="150000"
C2="-52.00"
D2="office supplies"

但在第一张表中,我们需要从 A11:12、B11:12、C11:12、D11:12 等开始粘贴这些值(尽管第二天可能有 40 行而不是 2 行)。

【问题讨论】:

  • sheet1 上的数据如何变化?它是自动更新的,还是真的有人在 Excel 中更改数据?
  • @BruceWayne 它是由其他公式完成的(基本上有一个报告转储,然后是宏将其格式化为它需要在工作表 2 上的状态,然后我希望将其复制到不同的工作表作为值)。

标签: excel vba


【解决方案1】:

使用工作表的保留.CodeName property 作为变量名可能不是一个好主意;尤其是它甚至不是该工作表的代号。

Sub FillInCoding()
    Dim lr As Long, JE As Worksheet, ws2 As Worksheet

    Set JE = ThisWorkbook.Worksheets(1)
    Set ws2 = ThisWorkbook.Worksheets(2)

    lr = ws2.Range("R65536").End(xlUp).Row

    With JE
        .Range("A11:G98567").ClearContents

        .Range("A11:B11").Resize(lr - 1, 2) = ws2.Range("AM2:AN" & lr).Value
        .Range("E11").Resize(lr - 1, 1) = ws2.Range("R2:R" & lr).Value
        .Range("G11").Resize(lr - 1, 1) = ws2.Range("AO2:AO" & lr).Value
    End With

End Sub

我选择了使用一些修改后的工作表引用进行直接价值转移。前两列 AM & AN 到 A 和 B 可以加倍。请参阅我关于设置最后一行的评论。

请参阅How to avoid using Select in Excel VBA macros,了解更多摆脱依赖选择和激活来实现目标的方法。

【讨论】:

  • 比我的回答简洁多了。 (对不起,题外话,但我应该完全删除我的答案,还是留下它,因为它,即使是切线,回答了问题的一部分?)
  • @BruceWayne - 我必须编辑我的问题以说明这种转变。 OP 可以从所有答案中受益。
  • 啊,好吧。您是否建议将其添加到 Sheet1 的 Worksheet Change 事件中?如果在 Sheet1 中进行了更改,OP 似乎希望自动复制信息……或者有更好的方法吗? (我在想,如果用户自己正在更改代码,让他们每次都运行宏......)
  • @BruceWayne - 这似乎不是我要放入 Worksheet_Change 的内容。发生了太多事情,用户所要做的就是更改或删除一个值来启动它。可能最好将其保留为按需运行的宏子。
【解决方案2】:

由于您的代码编写方式,可能会出现几个不同的问题。

首先,您复制的范围大于您尝试粘贴的区域。在您的示例中:

 Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow).PasteSpecial xlPasteValues

如果您的 lastRow 变量有 100 个条目,那么您只需复制 99 个单元格 (2-100)。然而,您试图将它们粘贴到只有 90 个单元格 (11-100) 的空间中。一个糟糕但非常简单但不复杂的解决方法是简单地将差异添加到 lastrow 变量:

 Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow **+ 9**).PasteSpecial xlPasteValues

但更好的是,您不需要明确说明要粘贴到的范围。您可以只给出目标单元格,无论内容长度如何,它都会粘贴在那里:

Worksheets(2).Range("AM2:AM" & lastRow).Copy
Worksheets(1).Range("A11").PasteSpecial xlPasteValues

此外,您正在做的其他一些事情可能会在以后给您带来一些问题。 您将 lastRow 范围设置为第一个电子表格上 R 列的长度。当基于您复制和粘贴的方式时,我猜您希望您的 lastRow 长度成为您正在积极复制的列的长度。您可以通过以下方式获得:

lastRow = Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Row

并将“A”更改为您的目标列。然后在每次复制新行时重置最后一行变量。一个例子是:

 lastRow = Sheets("Sheet2").Range("AM" & Rows.Count).End(xlUp).Row
 Sheets("Sheet2").Range("AM2:AM" & lastRow).Copy
 Sheets("Sheet1").Range("A11").PasteSpecial xlPasteValues

 lastRow = Sheets("Sheet2").Range("AN" & Rows.Count).End(xlUp).Row
 Sheets("Sheet2").Range("AN2:AN" & lastRow).Copy
 Sheets("Sheet1").Range("B11").PasteSpecial xlPasteValues

最后,我将 Worksheets(1) 更改为按名称调用工作表,因为我发现此方法更直观,并且允许您识别您正在复制和粘贴的工作表,即使在您或用户已移动页面顺序。

所以您的最终代码将如下所示。肯定有更简洁的方法来编写所有这些(包括将大量重复的代码,如工作表转换为变量),但我认为这是您当前正在编写的代码的合乎逻辑的下一步:

Dim LastRow as long

     lastRow = Sheets("Sheet2").Range("AM" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("AM2:AM" & lastRow).Copy
     Sheets("Sheet1").Range("A11").PasteSpecial xlPasteValues

     lastRow = Sheets("Sheet2").Range("AN" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("AN2:AN" & lastRow).Copy
     Sheets("Sheet1").Range("B11").PasteSpecial xlPasteValues

     lastRow = Sheets("Sheet2").Range("R" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("R2:R" & lastRow).Copy
     Sheets("Sheet1").Range("E11").PasteSpecial xlPasteValues

     lastRow = Sheets("Sheet2").Range("A0" & Rows.Count).End(xlUp).Row
     Sheets("Sheet2").Range("A02:A0" & lastRow).Copy
     Sheets("Sheet1").Range("G11").PasteSpecial xlPasteValues

【讨论】:

  • 请阅读那些打败我的人的回答 :)。看起来大多数人都在提倡价值到价值的转移。如果您只是移动值,则此方法可能会更快,但是,您需要确保范围相同(即,您不能像我一样只粘贴到单个单元格中。)所以如果您从范围内应对2-lastrow 并粘贴到范围 11-Last row,您必须将单元格中的差异添加到最后一行变量。 :Sheets("Sheet1").Range("A11:A" & lastRow + 9).value = Sheets("Sheet2").Range("AM2:AM"& lastRow).value.
  • 一个用户确实使用了 resize 方法(我不太熟悉),所以这可能是一种有效的方法。此外,请确保您的 lastRow 是您想​​要的长度。 IE。如果您希望它捕获 AM 列中的所有数据,则需要在复制之前将变量重置为该列中的最后一个单元格,而不仅仅是在脚本顶部设置一次。
【解决方案3】:

需要注意的是,您可以跳过复制/粘贴部分,只需将两个范围值设置为彼此相等:

Worksheets(2).Range("AM2:AM" & lastRow).Copy
 Worksheets(1).Range("A11:A" & lastRow).PasteSpecial xlPasteValues

一样
Worksheets(1).Range("A11:A" & lastRow).Value = Worksheets(2).Range("AM2:AM" & lastRow).Value

至于使用不同的工作表,我强烈建议创建两个变量来保存它们,并直接(并明确地)处理每个工作表:

Sub test()
Dim ws1 As Worksheet, ws2 As Worksheet

' Let's set "Sheet1" to be ws1, and "Sheet2" to be ws2
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")

'Now, use WITH to work in a specific sheet.
With ws1
    .Range("A1").Value = "Cell A1" ' Note the . at the beginning of Range().  This makes sure that the range you're using is
    ' on sheet1, not any other sheet

    'The next line is the SAME as using Range("A1"), only using Cells(row,Column). NOTE the . before Range() AND Cells()
    .Range(.Cells(1, 1), .Cells(1, 1)).Value = "Cell A1"
End With

With ws2
    .Range("A1").Value = "Sheet2, Cell A1"
End With

'Let's now say you want sheet1, A1 to be put in Sheet2, A1:

' [copy TO range] = [where you want to copy FROM]
ws2.Range("A1").Value = ws1.Range("A1").Value

'Or, of course, we can use ranges
ws2.Range("A1:B100").Value = ws1.Range("A1:B100").Value
' is same as
ws2.Range(ws2.Cells(1, 1), ws2.Cells(100, 2)).Value = ws1.Range(ws1.Cells(1, 1), ws1.Cells(100, 2)).Value
' is same as
With ws2
    .Range(.Cells(1, 1), .Cells(100, 2)).Value = ws1.Range(ws1.Cells(1, 1), ws1.Cells(100, 2)).Value
End With

End Sub

希望以上内容很清楚,您可以了解如何编辑宏以确保提取数据并将数据粘贴到正确的页面。

编辑:我刚刚注意到您确实已经设置了一些工作表 - 所以只需将上面的内容与这些一起使用。重读后,我想我解释了你的问题(或至少部分)......

Edit2:哇,好吧,我完全误解了你的问题。对于那个很抱歉!我想我会把它留在这里,因为它确实解释了设置值,没有复制/粘贴。对不起,伙计,我再看看你的问题。 (如果更熟悉 StackOverflow 的人可以告诉我,我应该将这个答案保留在这里,还是完全删除?论坛指南是什么?)

【讨论】:

    【解决方案4】:

    我很欣赏给出的答案,我从他们所有人那里学到了一些东西。我最终需要做的是让宏运行并完全正确地复制数据:

    calcState = Application.Calculation
    'when this was turned off before the next coding it didn't copy over the company name properly (column A)
    Application.Calculation = calcState
    
    'Puts the coding into the JE Sheet
    Call FillInCoding
    
    'turning it back off to speed up the final bits of coding
    Application.Calculation = xlCalculationManual
    

    我不太确定为什么将计算模式设置为手动而不是自动会导致有时在我在列 A 中列出的示例中列没有正确复制而不是复制(从第二张纸到第一张) 像

    A1 "XYZ"
    A2 "YZA"
    A3 "GHI"
    A4 "XYZ"
    

    它正在放:

    A1 "XYZ"
    A2 "XYZ"
    A3 "XYZ"
    A4 "XYZ"
    

    抱歉,我认为我不是最清楚正在发生的事情,我的编码总体上是独立工作的,但@Jeeped 显然更清洁、更简单,而且更好。我对一般编码和这个网站还是很陌生。

    我想到了将这些选项从 https://blogs.office.com/2009/03/12/excel-vba-performance-coding-best-practices/ 自动关闭为手动的想法,它确实使父宏按照那里的建议明显更快,但我想这可能会导致问题。

    【讨论】:

    • 你记得最后重新打开计算吗? Application.Calculation = xlCalculationAutomatic?如果您要复制值,我不确定它们为什么不能正确复制 - 但是,如果是公式,则公式不会自行更新(计算),直到您告诉 Excel (通过xlCalculationAutomatic)。此外,如果您将范围设置为相等,则可以完全避免复制/粘贴。您可以按照现在的方式发布您的代码吗 - 关闭计算并复制,以便我可以尝试重新创建您的错误?
    • @BruceWayne 重新创建它只需使用 Jeeped 上面使用的子程序,让它被另一个子程序调用,就像我提到的'Sub MasterSub() calcState = Application.Calculation Application.Calculation = xlCalculationManual Call FillInCoding Application.Calculation = calcState End Sub' 当这样做时,它不会正确地将公司代码(A 列,它们都是 3 个字母代码)复制到第二张表中。它复制一个并将相同的东西放在它粘贴到的所有区域中。但是如果它保持在自动计算(excel默认值),那么它确实有效。
    • @Jeeped 上面提到的编码本身也很有魅力,Calculation=Manual vs Calcuation=Auto(这是默认设置)有点奇怪。
    猜你喜欢
    • 1970-01-01
    • 2023-01-31
    • 1970-01-01
    • 1970-01-01
    • 2022-08-16
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多