【问题标题】:Excel VBA - Function Stops after using FindNext (Works in Subroutine)Excel VBA - 使用 FindNext 后功能停止(在子程序中工作)
【发布时间】:2019-08-27 01:41:33
【问题描述】:

我正在编写一个函数,该函数将在名为“Usage”的指定工作表的列中查找所有单元格,该工作表将有多个单元格与我找到的内容相匹配。问题是当我尝试调用 FindNext 时,VBA 停止工作而不会引发任何错误。 但是,如果我将函数更改为子例程,它会完美运行,因为 Debug 会显示找到的所有 10 个单元格地址。但是我需要使用一个函数,因为我将根据找到的结果返回一个值。当我使用 F8 单步执行时,在调试行使用 FindNext 后,我​​的函数立即停止 - 至少有 10 个值与传入的项目匹配。完全混淆了为什么它作为子例程而不是函数工作。

Function FindAllCells(item As String) As String

  Dim searchRange As Range
  Dim foundItem As Range
  Dim firstCellAddress As String

  Set searchRange = Sheets("Usage").Range("A:A")
  Set foundItem = searchRange.Find(What:=item, LookAt:=xlWhole)

  If foundItem Is Nothing Then
    Exit Function
  End If

  firstCellAddress = foundItem.Address

  Do 
    Set foundItem = searchRange.FindNext(foundItem)
    Debug.Print foundItem.Address
  Loop While firstCellAddress <> foundItem.Address

  FindAllCells = foundItem.Offset(0,2)
End Function  

【问题讨论】:

标签: excel vba


【解决方案1】:

问题是 .FindNext 在 UDF 中不起作用(从 Excel 公式调用)。

您可以改用另一个 .Find 来解决此问题。

其他几点:

  1. 我也会传入搜索范围,使其更加灵活
  2. 一些Find 参数,如果未指定,则从上次使用Find 中获取值,无论是由VBA 还是用户。 See here
Function FindAllCells(searchRange As Range, item As String) As String
    Dim foundItem As Range
    Dim firstCellAddress As String

    Set foundItem = searchRange.Find(What:=item, After:=searchRange.Cells(searchRange.Cells.Count), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows)
    If foundItem Is Nothing Then Exit Function

    firstCellAddress = foundItem.Address
    Do
        Set foundItem = searchRange.Find(What:=item, After:=foundItem, LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows)
        Debug.Print foundItem.Address
    Loop While firstCellAddress <> foundItem.Address

    FindAllCells = foundItem.Offset(0, 2)
End Function

像这样称呼

=FindAllCells(Usage!$A:$A,"item")

也就是说,考虑到你的一些 cmets 对你更大的目标,我认为你是在一个受伤的世界

【讨论】:

    【解决方案2】:

    我今天早些时候需要一个可以在单个单元格中显示每个匹配项的 UDF。我记得写了一个可以做到这一点的函数,我回到这里是为了重用我的代码。

    对我来说不幸的是,它是针对 OPs 问题的,对于我当前的需求不够灵活。

    我的损失就是你的收获!

    所以现在,我已经完全重写了函数以接受Range.Find 中的几乎所有参数。据我所知,工作表中没有像 xlByColums 这样的 Excel 枚举器。我不可能很快记住所有这些,所以我通过将大多数参数转换为布尔值使事情变得更容易。 searchByColumnfindPartialMatchreverseSearchDirectioncaseSensitivelookAtFormat 均通过 True/False 设置,但 xlFindLookIn 仍需要常量值来指定 cmets/formulas/values。

    这还不是全部……

    输出不必是匹配项。它可以是匹配项的地址或偏移量。想象一下,如果VLOOKUPINDEXMATCH 将它们匹配的所有内容放入一个单元格中,这基本上就是偏移参数所允许的。在重要的时候,输出可以包括匹配的总数。

    但是等等,还有更多!

    一个小但非常重要的选项,分隔符可以通过参数从工作表中设置。

    我只想说,这个函数可以有多种使用方式,我不可避免地会在某个时候回来重用它。

    请注意,我使用了具有两个不同条件退出点的无限循环,逻辑应该清晰易懂,但如果您开始编辑代码,则需要注意这一点。

    Public Function FindAllMatches(ByVal searchFor As String, ByVal searchRange As Range, _
                                        Optional ByVal columnOffset As Long, _
                                        Optional ByVal rowOffset As Long, _
                                        Optional ByVal countMatches As Boolean, _
                                        Optional ByVal showAddresses As Boolean, _
                                        Optional ByVal delimiter As String, _
                                        Optional ByVal findPartialMatch As Boolean = True, _
                                        Optional ByVal searchByColumn As Boolean = False, _
                                        Optional ByVal reverseDirection As Boolean = False, _
                                        Optional ByVal caseSensitive As Boolean = False, _
                                        Optional ByVal lookAtFormat As Boolean = False, _
                                        Optional ByVal lookInside As XlFindLookIn = xlValues _
                                        ) As String
    
        Dim firstMatchCellAddress As String
        Dim searchResult As Range
        Dim returnString As String
        Dim matchCount As Long
        Dim includePartial As Single: includePartial = -findPartialMatch + 1
        Dim previousDirection As Single: previousDirection = -reverseDirection + 1
        Dim searchAxis As Single: searchAxis = -searchByColumn + 1
    
        If Not CBool(Len(delimiter)) Then delimiter = Chr(44)
        Set searchResult = searchRange
    
        Do
            Set searchResult = searchRange.Find(What:=searchFor, After:=searchResult.Cells(searchResult.Cells.Count), LookIn:=lookInside, _
                    LookAt:=includePartial, SearchOrder:=searchAxis, SearchDirection:=previousDirection, _
                    MatchCase:=caseSensitive, SearchFormat:=lookAtFormat)
    
            Select Case True
    
                Case searchResult Is Nothing
                    Exit Do
    
                Case firstMatchCellAddress = searchResult.Address
                    Exit Do
    
                Case CBool(Len(returnString))
                    If showAddresses Then
                        returnString = returnString & delimiter & searchResult.Offset(rowOffset, columnOffset).Address
                    Else
                        returnString = returnString & delimiter & searchResult.Offset(rowOffset, columnOffset)
                    End If
    
                Case Else
                    If showAddresses Then
                        returnString = searchResult.Offset(rowOffset, columnOffset).Address
                    Else
                        returnString = searchResult.Offset(rowOffset, columnOffset)
                    End If
    
                    firstMatchCellAddress = searchResult.Address
    
            End Select
    
            matchCount = matchCount + 1
    
        Loop While True
    
        If countMatches Then
            If CBool(matchCount) Then
                returnString = matchCount & delimiter & returnString
            Else
                returnString = 0
            End If
        End If
    
        FindAllMatches = returnString
    
    End Function
    

    【讨论】:

    • 非常感谢。不知道 `.FindNext' 在 UDF 中不起作用。它现在可以完美地找到所有匹配项。
    • @user2382843 很高兴为您提供帮助。老实说,我也不知道,直到我尝试复制您的问题。值得庆幸的是,该问题已被社区充分记录,并且易于解决。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-14
    • 1970-01-01
    • 2017-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多