Excel VBA - 使用 FindNext 后功能停止(在子程序中工作)
Posted
技术标签:
【中文标题】Excel VBA - 使用 FindNext 后功能停止(在子程序中工作)【英文标题】:Excel VBA - Function Stops after using FindNext (Works in Subroutine) 【发布时间】: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
【问题讨论】:
评论不用于扩展讨论;这个对话是moved to chat。 【参考方案1】:问题是 .FindNext
在 UDF 中不起作用(从 Excel 公式调用)。
您可以改用另一个 .Find
来解决此问题。
其他几点:
-
我也会传入搜索范围,使其更加灵活
一些
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 枚举器。我不可能很快记住所有这些,所以我通过将大多数参数转换为布尔值使事情变得更容易。 searchByColumn
、findPartialMatch
、reverseSearchDirection
、caseSensitive
和 lookAtFormat
均通过 True/False 设置,但 xlFindLookIn
仍需要常量值来指定 cmets/formulas/values。
这还不是全部……
输出不必是匹配项。它可以是匹配项的地址或偏移量。想象一下,如果VLOOKUP
或INDEXMATCH
将它们匹配的所有内容放入一个单元格中,这基本上就是偏移参数所允许的。在重要的时候,输出可以包括匹配的总数。
但是等等,还有更多!
一个小但非常重要的选项,分隔符可以通过参数从工作表中设置。
我只想说,这个函数可以有多种使用方式,我不可避免地会在某个时候回来重用它。
请注意,我使用了具有两个不同条件退出点的无限循环,逻辑应该清晰易懂,但如果您开始编辑代码,则需要注意这一点。
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 很高兴为您提供帮助。老实说,我也不知道,直到我尝试复制您的问题。值得庆幸的是,该问题已被社区充分记录,并且易于解决。以上是关于Excel VBA - 使用 FindNext 后功能停止(在子程序中工作)的主要内容,如果未能解决你的问题,请参考以下文章
Excel VBA Range.FindNext v Range.Find - 我错过了啥?