【问题标题】:How to pass a value from one sub to another in Excel VBA如何在 Excel VBA 中将值从一个子传递到另一个子
【发布时间】:2015-11-24 14:39:36
【问题描述】:

在我的宏中,我有一个子例程(使用 for 循环)遍历表的行并根据 S 列中的内容在 V 列中写入注释。在此循环期间,它还计算它的次数在 S 列中显示“新建”。现在我想将此值向上传递到主宏并向下传递到另一个子例程。我该怎么做或者我的方法是错误的?

如果我理解正确,通常不可能从 excel VBA 中的 subs 中返回值,但您可以从函数中返回值。但是我认为函数在这里不合适(我可能误解了函数在 VBA 中的工作方式和/或没有充分发挥它们的潜力!)。

那么我如何将变量/值从 sub 中偷运到另一个中?是唯一的选项Global 还是Public 声明?

这是我的代码的一个非常粗略的示例:

Sub MainMacro ()
  Call CommentSub
  Call NumberOfRowsToCopy
End sub

Sub CommentSub
  Dim Counter As Integer
  For Counter = 1 to 500
    If Cells(Counter, "S") = "New" Then
      NewOrderLineCounter = NewOrderLineCounter + 1
      Cells(Counter, "V").Select
      ActiveCell.FormulaR1C1 = "New Line"
    End If
  Next Counter
End sub

Sub NumberOfRowsToCopy
  ActiveSheet.Range("$A$12:$T$1001").AutoFilter Field:=16, Criteria1:= _
    "New"
  ActiveSheet.Range("B15:N" & NewOrderLineCounter).SpecialCells(xlCellTypeVisible).Select
End sub

(顺便说一句,我知道可能有“更好”的方法来获取需要在此处复制的行数(从而消除了在 subs 之间传递值的需要)但我想我已经尝试了所有这些并且没有工作。我认为它是 excel 表的格式,但这是另一个问题,你必须使用你得到的东西,对吧?)

【问题讨论】:

  • 我不明白你为什么不能使用函数?您应该可以毫无问题地将它们中的第一个设为函数,并将计数器的结果作为 CommentSub 的输出作为 NumberOfRowsToCopy 的输入传递,并使 NumberOfRowsToCopy 具有 1 个计数器的输入值
  • 我的印象是我误解了函数在 VBA 中的工作方式。从我读到的内容(在帮助文件等中),我了解到函数是用来进行计算的。该示例几乎总是给出函数 x 和 y,它会返回值 z,例如面积或体积之类的。我没有看到它说的任何地方:“在一个函数中,你可以完全按照你在 sub 中所做的,但也返回一个值”。
  • 到底算什么?一系列运算得出某个答案,这是否意味着您仅限于数学运算符?不,不是的。在 Vba 中,function 和 sub 之间的主要区别在于,函数是子例程,它们向调用者返回一个值,而 subs 只是被调用并没有返回值而结束。与 C 相比,subs 是 void 函数。

标签: vba excel


【解决方案1】:

如何在子(和函数)之间传递和更新变量

Option Explicit

Public Sub MainSub()

    Dim local_1 As Long
    Dim local_2 As Long

    local_1 = 0
    local_2 = 0

    setVal local_1              'Sub setVal() updates local_1
    MsgBox local_1              'result: 1

    putVal local_1              'Sub putVal() doesn't update local_1
    MsgBox local_1              'result: 1

    local_2 = getVal(local_1)   'Function getVal() updates local_1 and local_2
    MsgBox local_1              'result: 2
    MsgBox local_2              'result: 3

End Sub


Public Sub setVal(ByRef val As Long)    'pass ByRef (not a copy)
    val = val + 1
End Sub

Public Sub putVal(ByVal val As Long)    'pass ByVal (a copy)
    val = val + 1
End Sub

Public Function getVal(ByRef val As Long) As Long
    val = val + 1               'updates val
    getVal = val + 1            'doesn't update val (returns a new value)
End Function

.

我会用自动过滤器替换 CommentSub() Sub 中的 For 循环:

Public Sub MainMacro()
    Dim newOrderRows As Long

    Call CommentSub(newOrderRows)
    Call NumberOfRowsToCopy(newOrderRows)
End Sub

Public Sub CommentSub(ByRef newOrderRows As Long)
    Dim vRng As Range

    With ActiveSheet.UsedRange
        .AutoFilter Field:=19, Criteria1:="New"
        Set vRng = .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count).Columns("V")

        vRng.SpecialCells(xlCellTypeVisible) = "New Line"
        newOrderRows = vRng.SpecialCells(xlCellTypeVisible).Count
        .AutoFilter
    End With
End Sub

Public Function NumberOfRowsToCopy(ByVal newOrderRows As Long) As Long
    Dim x As Long

    With ActiveSheet
        .Range("A12:T" & .UsedRange.Rows.Count).AutoFilter Field:=16, Criteria1:="New"
        x = .Range("N15:N" & 15 + newOrderRows).SpecialCells(xlCellTypeVisible).Count
    End With

    NumberOfRowsToCopy = x
End Function

【讨论】:

    【解决方案2】:

    我不确定你为什么觉得某个函数不适合这里。

    它们和sub一样,只是有一个返回值。

    Private Sub MainMacro()
        Dim lReturn As Long
    
        'Get the return from the CommentSub
        lReturn = CommentSub
    
        'Pass that to the nextsub
        NumberOfRowsToCopy (lReturn)
    End Sub
    
    Function CommentSub() As Long 'Declare the return type after the function
      Dim NewOrderLineCounter As Long
      Dim Counter As Integer
      For Counter = 1 To 500
        If Cells(Counter, "S") = "New" Then
          NewOrderLineCounter = NewOrderLineCounter + 1
          Cells(Counter, "V").Select
          ActiveCell.FormulaR1C1 = "New Line"
        End If
      Next Counter
    
      'Here you set the return value of the funtion
      CommentSub = NewOrderLineCounter
    End Function
    
    Sub NumberOfRowsToCopy(lCount As Long) 'Declare the variable being passed to the sub.
    Dim NewOrderLineCounter As Long
    NewOrderLineCounter = lCount
      ActiveSheet.Range("$A$12:$T$1001").AutoFilter Field:=16, Criteria1:= _
        "New"
      ActiveSheet.Range("B15:N" & NewOrderLineCounter).SpecialCells(xlCellTypeVisible).Select
    End Sub
    

    或者,如果您想使用公共变量路由,请在表单或模块代码的顶部声明它们。最重要的是功能和子。

    'Declared like this it can be accessed by any sub or function in this module or form.
    Private NewOrderLineCounter as Long
    
    'Declared like this it can be accessed by any sub or function in this module or form and from others. Although I think if it is in a form it will not be accessible from modules.  For that you can create a module called globals and declare it there as public.
    Public NewOrderLineCounter as Long
    
    Sub MainMacro ()
      Call CommentSub
      Call NumberOfRowsToCopy
    End sub
    
    Sub CommentSub
      Dim Counter As Integer
      For Counter = 1 to 500
        If Cells(Counter, "S") = "New" Then
          NewOrderLineCounter = NewOrderLineCounter + 1
          Cells(Counter, "V").Select
          ActiveCell.FormulaR1C1 = "New Line"
        End If
      Next Counter
    End sub
    
    Sub NumberOfRowsToCopy
      ActiveSheet.Range("$A$12:$T$1001").AutoFilter Field:=16, Criteria1:= _
        "New"
      ActiveSheet.Range("B15:N" & NewOrderLineCounter).SpecialCells(xlCellTypeVisible).Select
    End sub
    

    【讨论】:

    • 我可能误解了函数在 VBA 中的工作方式和/或没有充分发挥它们的潜力。随时教育我!
    • 子函数和函数是一样的,只是函数返回一个值。他们都接受论点。
    【解决方案3】:

    如果你真的坚持保持原样,只需将值保存在这样的一张表中

    Sub CommentSub
      Dim Counter As Integer
      For Counter = 1 to 500
        If Cells(Counter, "S") = "New" Then
          NewOrderLineCounter = NewOrderLineCounter + 1
          Cells(Counter, "V").Select
          ActiveCell.FormulaR1C1 = "New Line"
        End If
      Next Counter
      ws.Cells(i,j).Value = Counter
    End sub
    

    ws 是您要保留的工作表,ij 是单元格行和列的值,然后像这样取它,然后清除(或不清除)

    Sub NumberOfRowsToCopy
      myCounter = ws.Cells(i,j).Value
      ws.Cells(i,j).clear
      ActiveSheet.Range("$A$12:$T$1001").AutoFilter Field:=16, Criteria1:= _
        "New"
      ActiveSheet.Range("B15:N" & NewOrderLineCounter).SpecialCells(xlCellTypeVisible).Select
    End sub
    

    但我又觉得没有真正的理由不使用函数

    【讨论】:

      猜你喜欢
      • 2020-05-08
      • 1970-01-01
      • 2018-10-22
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多