如何在 VBA 中调用带有列表和另一个参数的函数?

Posted

技术标签:

【中文标题】如何在 VBA 中调用带有列表和另一个参数的函数?【英文标题】:How to call a function with a list and one other argument in VBA? 【发布时间】:2019-10-01 05:42:42 【问题描述】:

我的目标是在 VBA Excel 中制作一个自动修复列名的小程序/宏。我想这样做的方法是为每个单独的列名cell 设置Subs。这些Subs 中的每一个都有一个Array 的字符串,其中包含将要搜索的关键字。如果单元格值恰好是字符串之一,则该值将是固定的。但是,如何修复它们取决于列名,这就是为什么需要单独创建每个子项的原因。

现在,我创建了 Sub,它调用所有个人 cell 修复 Subs。我还制作了 Function 来测试相应的 cell 是否包含关键字之一。但是,我不知道如何正确地将有关可搜索字符串的Array 和相应的cell 的信息传递给Sub。我在尝试调用 FindMatch() 函数时收到“运行时错误 '13':类型不匹配”。

Function FindMatch(textArr() As String, cell As Range) As Boolean
    FindMatch = False
    Dim testCell As Range
    Dim i As Integer

' Range.Find() each string in the Array. If one of the Strings is found, FindMatch is true
    For i = 0 To UBound(textArr)
        Set testCell = cell.Find(What:=textArr(i), LookIn:=xlValues, LookAt:=xlWhole)

        If Not testCell Is Nothing Then
            FindMatch = True
        End If

    Next i

End Function

从这里调用:

Sub FindCr(cell As Range)

    Dim match As Boolean
    match = False

    Dim textArr() As String
    ' I get the type mismatch error here.
    textArr = Array("Cr", "Bag ", " Dog")

    match = FindMatch(textArr, cell)

    If match Then
' Do stuff depending on the column, for example:
        HighlightRange cell
    End If
End Sub

这里是调用各个查找和修复 Subs 的主 Sub。它也从一个大的主循环中调用,该循环遍历整个列名单元格范围。

Public Sub fixColumnNames(cell As Range)
    'Do find-and-fix functions for all possible column names
    FindCr cell
End Sub

注意,我不想使用 ParamArray,因为我还需要在 FindMatch() 函数中传递 cell。我相信 ParamArray 仅用于调用中存在未定义数量的参数时。我不希望 cell 进入 ParamArray。

我尝试将 FindMatch 和 Dim textArr() 的声明更改为 As Variant,但它确实弄乱了 Range.Find() 函数。当它被赋予Variant 时,它开始匹配各种错误的关键字。像这样:

Function FindMatch(textArr() As Variant, cell As Range) As Boolean

Dim textArr() As Variant
textArr = Array("Cr", "Bag ", " Dog")
match = FindMatch(textArr, cell)

因此,如果您有其他方法可以将字符串列表传递给具有其他附加参数的函数,请教我。或者,如果您知道导致问题的原因,我们将不胜感激。

【问题讨论】:

FindMatch 哪里出现类型不匹配? @Dean 当我在match = FindMatch(textArr, cell) 调用FindMatch 时遇到类型不匹配问题 “我不想使用 ParamArray,因为我还需要传递单元格”。你仍然可以这样做,ParamArray 只需要成为最后一个参数。 Sub fixColumnNames(cell as range, ParamArray arr() as variant). “当它被赋予 Variant 时,它开始匹配各种错误的关键字”... 事实上,您将数组“列表”中的每个元素都包含在 double-引号自动使每个元素成为字符串 - 即使您声明为类型变体。试试看,MsgBox TypeName(textArr(0)) @K.Dᴀᴠɪs 逃避 - 即使有其他参数,您仍然可以将数组传递给函数。如果您想传递该数组的各个元素,那么我建议始终将该元素存储在类型变量中,然后再通过以避免类型不匹配。 【参考方案1】:

我不确定您为什么会遇到问题,但这对我来说效果很好。由于您在使用 ParamArray 时遇到问题,因此我继续将其合并到代码中以进行演示。

Sub FindCr(cell As Range)

    If FindMatch(cell, "Cr", "Bag ", " Dog") Then
        MsgBox "There was a match!"
    End If

End Sub

Function FindMatch(ByVal cell As Range, ParamArray textArr() As Variant) As Boolean

    Dim i As Long
    For i = 0 To UBound(textArr)

        If Not cell.Find(What:=textArr(i), LookIn:=xlValues, LookAt:=xlWhole) Is Nothing Then
            FindMatch = True
            Exit Function
        End If

    Next

End Function

为了便于阅读,我将它缩短了一点。您从未展示过您为 Cell 参数传递的内容,但请确保您完全符合范围。

Dim Cell As Range
Set Cell = Thisworkbook.Worksheets(1).Range("A1")

将是完全限定范围的示例。

【讨论】:

@K.Davis 我也不确定我为什么会遇到问题,但我开始怀疑这与未正确重置变量有关?出于某种原因,cell.Find() 函数始终返回值为“CR”的 Range,这导致 If 条件始终为真。我尝试更改变量名称,给 Find() 函数一个参数 what:= "string" 并在项目中搜索具有相同名称的变量的其他实例,但我找不到任何东西。 Debug.Print 说 Find 函数总是返回一个单元格。甚至重新启动程序也无济于事。 Wtf vba。 它返回带有 Cr 值的范围,因为你告诉它。它在您的数组中硬编码以供查找。 @K.Davis 和cell 参数总是得到Application.Selection,我通过单击宏按钮来调用它。所以我手动选择一个范围,单击宏按钮,程序应该修复范围中的文本。 不,我的意思是它不返回“Cr”,它返回“CR”,即使我只传递了Function 一个空单元格。在这种情况下,它绝对不应该返回任何东西。昨天我做一些区分大小写的测试时,其中有一个值为“CR”的单元格,但它一定是卡住或未重置。 @K.Davis 我尝试更改函数的 Find() 部分。我总是将单个单元格作为Cell 传递,因此我可以轻松访问该值。我做了If StrComp(textArr(i), cell.Value2, vbTextCompare) = 0 Then FindMatch = True 并且效果很好。 Find() 应该做同样的事情,但由于某种原因它没有。 Find() 仍然有一些非常错误的地方,但我稍后会研究它。我用 Python 和 Java 做过编程,这种事情从来没有发生在我身上……【参考方案2】:

您不能以这种方式影响字符串数组:

textArr = Array("Cr", "Bag ", " Dog")

要使用那种形式的矫揉造作,textArr 必须是一个变体。

如果还想用string,就用redim和single的做作。例如:

dim textArr() as String
...
Redim Preserve textArr(1)
textArr(0) = "Cr"
...
Redim Preserve textArr(2)
textArr(1) = "Bag"

以此类推,或者你可以在循环中进行,或者如果你知道最终数组的长度,只需用正确的长度将其调暗

【讨论】:

但这不会使textArr Variant 类型中的项目吗?正如我在文中所述,将项目设置为 Variant 类型会导致 Range.Find() 无法正常工作。你知道如何解决这个问题吗? 您可以尝试修改 FindMatch() 以接受变量数组并在循环中使用 What:cStr(textArr(i)) 将数据转换为字符串 我尝试修改为 What:cStr(textArr(i)) 仍然无法正常工作。我尝试调试这些值并查看为什么 Find 函数返回匹配项,这就是我得到的结果: 数组值:这永远不会是 TRUE 单元格值:不为真 FindMatch 为:False 数组值:Cr 单元格值:不为真 FindMatch 为:True 数组值:Dog Cell 值:不正确 FindMatch 是:True 我不明白该函数有什么问题,它说“Cr”与“Dont be true”完全匹配...... 嗯,看来这又是一个问题。您不再有错误 13,对吗?当遇到条件时,您应该退出循环。 好吧。我知道可以传递一个变体数组,但是它破坏了 Find 函数,所以我不想使用它。如果我给它单独的 String 对象,Find 函数就可以完美地工作。这告诉我传递的数组仍然有问题。

以上是关于如何在 VBA 中调用带有列表和另一个参数的函数?的主要内容,如果未能解决你的问题,请参考以下文章

vba中如何在一个function中 输入一个参数返回另一个参数

VBA的有参过程定义,形参用啥说明

(Excel)带有可选参数的vba程序将无法启动

带有 2 个对象参数的 Excel VBA 对象子调用给出编译错误:预期 =

透明地通过带有可变参数列表的函数

VBA 可选参数