在合并的单元格中找到值时,.FindNext不会“环绕”

时间:2019-06-27 15:23:19

标签: excel vba

因此,当我回答this question时,我发现自己不得不处理似乎是一个非常特殊的错误。

简而言之,当在工作表中搜索字符串的所有匹配项时,如果在合并的单元格中仅发现一个匹配项,并且此单元格是通过合并来自不同行的单元格来构造的,则.FindNext不会像it is supposed to那样环绕。

因此,如果在通过合并同一列的2个单元格而构成的单元格中找到关键字的第一个(也是唯一的)出现,则.FindNext将返回Nothing

怪异的部分是,如果出现多个事件,或者如果该单元格是通过合并同一行的单元格而构建的,它将按预期工作。

我已经通过引入Nothing的支票来解决这个问题,但是我很好奇为什么会这样。

是错误吗?我想念什么吗?

为完成起见,以下是代码:

Sub main()

Dim wbk As Workbook
Set wbk = ThisWorkbook
Debug.Print numOfOccurrences("test", wbk) 'call the search function and print the number of occurrences to the immediate window

End Sub

Public Function numOfOccurrences(keyword As String, wb As Workbook) As Long

Dim sht As Worksheet
Dim found As Range
Dim count As Long
Dim firstOccurence As String
count = 0

For Each sht In wb.Worksheets 'loop through sheets
    Set found = sht.Cells.Find(what:=keyword) 'search for the first occurrence if any
    If Not found Is Nothing Then 'if the keyword is found once, then we need to search for more occurrences
        firstOccurence = found.Address 'store the address of the first occurence
        Do
            Set found = sht.Cells.FindNext(found) 'search for the next occurrence in the same sheet
            count = count + 1 'keep track of the number of occurences
        If found Is Nothing Then
            GoTo DoneFinding    'this deals with what seems to be a bug when using .FindNext with merged cells
        End If
        Loop Until found.Address = firstOccurence 'repeat until the search goes full circle back to the first occurrence
    End If
DoneFinding:
Next sht
numOfOccurrences = count

End Function

1 个答案:

答案 0 :(得分:0)

从我所见,Excel将合并的区域视为单个单元格,因此,如果只有一个合并的区域具有该值,则FindNext不会找到另一个存在的单元格。您可以使用Range.MergeArea

来访问完整范围的地址。

这是一个通用的FindAll函数,如果您愿意的话,它将拉回某个范围内的合并区域。

Private Function FindAll(What, _
    Optional SearchWhat As Variant, _
    Optional LookIn, _
    Optional LookAt, _
    Optional SearchOrder, _
    Optional SearchDirection As XlSearchDirection = xlNext, _
    Optional MatchCase As Boolean = False, _
    Optional MatchByte, _
    Optional SearchFormat, _
    Optional IncludeMerged As Boolean = False) As Range

    'LookIn can be xlValues or xlFormulas, _
     LookAt can be xlWhole or xlPart, _
     SearchOrder can be xlByRows or xlByColumns, _
     SearchDirection can be xlNext, xlPrevious, _
     MatchCase, MatchByte, and SearchFormat can be True or False. _
     Before using SearchFormat = True, specify the appropriate settings for the Application.FindFormat _
     object; e.g. Application.FindFormat.NumberFormat = "General;-General;""-""" _
     Set IncludeMerged to 'True' to include all cells within a merged area

    Dim SrcRange As Range
    If IsMissing(SearchWhat) Then
        Set SrcRange = ActiveSheet.UsedRange
    ElseIf TypeOf SearchWhat Is Range Then
        Set SrcRange = IIf(SearchWhat.Cells.Count = 1, SearchWhat.Parent.UsedRange, SearchWhat)
    ElseIf TypeOf SearchWhat Is Worksheet Then
        Set SrcRange = SearchWhat.UsedRange
    Else: SrcRange = ActiveSheet.UsedRange
    End If
    If SrcRange Is Nothing Then Exit Function

    'get the first matching cell in the range first
    With SrcRange.Areas(SrcRange.Areas.Count)
        Dim FirstCell As Range: Set FirstCell = .Cells(.Cells.Count)
    End With

    Dim CurrRange As Range: Set CurrRange = SrcRange.Find(What:=What, After:=FirstCell, LookIn:=LookIn, LookAt:=LookAt, _
        SearchDirection:=SearchDirection, MatchCase:=MatchCase, MatchByte:=MatchByte, SearchFormat:=SearchFormat)

    If Not CurrRange Is Nothing Then
        Set FindAll = IIf(IncludeMerged = True, CurrRange.MergeArea, CurrRange)
        Do
            Set CurrRange = SrcRange.Find(What:=What, After:=CurrRange, LookIn:=LookIn, LookAt:=LookAt, _
            SearchDirection:=SearchDirection, MatchCase:=MatchCase, MatchByte:=MatchByte, SearchFormat:=SearchFormat)
            If CurrRange Is Nothing Then Exit Do
            If Application.Intersect(FindAll, CurrRange) Is Nothing Then
                Set FindAll = Application.Union(FindAll, IIf(IncludeMerged = True, CurrRange.MergeArea, CurrRange))
            Else: Exit Do
            End If
        Loop
    End If
End Function