What to do when autofilter in VBA returns no data?
Asked Answered
S

5

6

I am trying to filter a range of values and based on my criteria, at times I might have no data that fits my criteria. In that case, I do not want to copy any data from the filtered data. If there is filtered data, then I would like to copy it.

Here is my code:

With Workbooks(KGRReport).Worksheets(spreadSheetName).Range("A1:I" & lastrowinSpreadSheet)
    .AutoFilter Field:=3, Criteria1:=LimitCriteria, Operator:=xlFilterValues 'Do the filtering for Limit
     .AutoFilter Field:=9, Criteria1:=UtilizationCriteria, Operator:=xlFilterValues 'Do the filtering for Bank/NonBank
End With

'Clear the template
 Workbooks(mainwb).Worksheets("Template").Activate
 Workbooks(mainwb).Worksheets("Template").Rows(7 & ":" & Rows.Count).Delete

 'Copy the filtered data
 Workbooks(KGRReport).Activate
 Set myRange = Workbooks(KGRReport).Worksheets(spreadSheetName).Range("B2:H" & lastrowinSpreadSheet).SpecialCells(xlVisible)
 For Each myArea In myRange.Areas
     For Each rw In myArea.Rows
           strFltrdRng = strFltrdRng & rw.Address & ","
     Next
 Next

 strFltrdRng = Left(strFltrdRng, Len(strFltrdRng) - 1)
 Set myFltrdRange = Range(strFltrdRng)
 myFltrdRange.Copy
 strFltrdRng = ""

It is giving me an error at

Set myRange = Workbooks(KGRReport).Worksheets(spreadSheetName).Range("B2:H" & lastrowinSpreadSheet).SpecialCells(xlVisible)

When there is no data at all, it is returning an error: "No cells found".

Tried error handling like this post: 1004 Error: No cells were found, easy solution?

But it was not helping. Need some guidance on how to solve this.

Statued answered 12/4, 2016 at 2:23 Comment(2)
Just use something like: If Workbooks(KGRReport).Worksheets(spreadSheetName).Range("B2:H" & lastrowinSpreadSheet).RowHeight Then if nothing is shown, the height is 0 which will count as False (everything else will count as True)Tiercel
FWIW the linked question (and the link from that question since it was a dupe) should have provided a viable path out of this issue. That is, you need to handle the error if you know there is a chance it will be thrown. I give an approach to avoid the error handling but the other answers essentially say the same as that other question: handle the error.Fleshly
S
11

Try error handling like so:

Dim myRange As Range

On Error Resume Next
Set myRange = Range("your range here").SpecialCells(xlVisible)
On Error GoTo 0

If myRange Is Nothing Then
    MsgBox "no cells"
Else
    'do stuff
End If
Savor answered 12/4, 2016 at 3:38 Comment(2)
where is the jump statement 0? where should it be written?Statued
It isn't a jump statement, it clears the error handler.Savor
F
2

An approach without the error handling

It is possible to build the AutoFilter in a way that does not throw the error if nothing is found. The trick is to include the header row in the call to the SpecialCells. This will ensure that at least 1 row is visible even if nothing is found (Excel will not hide the header row). This prevents the error from jamming up execution and gives you a set of cells to check if data was found.

To check if the resulting range has data, you need to check Rows.Count > 1 Or Areas.Count > 1. This handles the two possible cases where your data is found directly under the header or in a discontinuous range below the header row. Either result means that the AutoFilter found valid rows.

Once you check that data was found, you can then do the desired call to SpecialCells on the data only without concern for an error.

Sample data [column C (field 2) will be filtered]:

random data

Sub TestAutoFilter()

    'this is your block of data with headers
    Dim rngDataAndHeader As Range
    Set rngDataAndHeader = Range("B2").CurrentRegion

    'this will knock off the header row if you want data only
    Dim rngData As Range
    Set rngData = Intersect(rngDataAndHeader, rngDataAndHeader.Offset(1))

    'autofilter
    rngDataAndHeader.AutoFilter Field:=2, Criteria1:=64

    'get the visible cells INCLUDING the header row
    Dim rngVisible As Range
    Set rngVisible = rngDataAndHeader.SpecialCells(xlCellTypeVisible)

    'check if there are more than 1 rows or if there are multiple areas (discontinuous range)
    If rngVisible.Rows.Count > 1 Or rngVisible.Areas.Count > 1 Then
        Debug.Print "found data"

        'data is available, this call cannot throw an error now
        Set rngVisible = rngData.SpecialCells(xlCellTypeVisible)

        'do your normal execution here
        '
        '
        '
    Else
        Debug.Print "only header, no data included"
    End If
End Sub

Result with Criteria1:=64

Immediate window: found data

enter image description here

Result with Criteria1:=0

Immediate window: only header, no data included

enter image description here

Other notes:

  • Code includes a separate variable called rngData if you want access to data without headers. This is just an INTERSECT-OFFSET to bump it one row down.
  • For the case where a result was found, code resets rngVisible to be the visible cells in the data only (skips header). Since this call cannot fail now, it is safe without error handling. This gives you a range that matches what you tried the first time but without the chance of getting an erorr. This is not required if you can process the original range rngVisible that includes the headers. If that is true, you can do away with rngData completely (unless you have some other need for it).
Fleshly answered 12/4, 2016 at 4:44 Comment(0)
F
1

since you use myRange as the real output of the filtering action you could go like follows

Dim wbKGRR As Workbook  '<== better set variable for workbooks you'll work with: it saves both typing time and possible errors
Dim ws As Worksheet  '<== better set variable for worksheets you'll work with: it saves both typing time and possible errors

'...


Set wbKGRR = Workbooks(KGRReport) '<== better set variable for workbooks: it saves both typing time and possible errors
Set ws = wbKGRR.Worksheets(spreadSheetName)  '<== better set variable for worksheets you'll work with: it saves both typing time and possible errors

With ws
    With .Range("A1:I" & lastrowinSpreadSheet)
        .AutoFilter Field:=3, Criteria1:=LimitCriteria, Operator:=xlFilterValues 'Do the filtering for Limit
        .AutoFilter Field:=9, Criteria1:=UtilizationCriteria, Operator:=xlFilterValues 'Do the filtering for Bank/NonBank
    End With
    If Application.WorksheetFunction.Subtotal(103, .Columns("B")) > 0 Then Set myRange = .Range("B2:H" & lastrowinSpreadSheet).SpecialCells(xlVisible) '<== myRange will be set only if filtering has left some visible cells
End With


'Clear the template
'Workbooks(mainwb).Worksheets("Template").Activate '<== no need to activate
Workbooks(mainwb).Worksheets("Template").Rows(7 & ":" & Rows.Count).Delete

'Copy the filtered data
' Workbooks(KGRReport).Activate '<== no need to activate
If Not myRange Is Nothing Then '<== "myRange" has been set properly if previous Autofilter method has left some visbile cells
    For Each myArea In myRange.Areas
        For Each rw In myArea.Rows
              strFltrdRng = strFltrdRng & rw.Address & ","
        Next rw
    Next myArea

    strFltrdRng = Left(strFltrdRng, Len(strFltrdRng) - 1)
    Set myFltrdRange = Range(strFltrdRng)
    myFltrdRange.Copy
    strFltrdRng = ""
End If

where I also suggested some workbook and worksheet variable settings to "ease" coding life

Fassett answered 12/4, 2016 at 5:42 Comment(0)
C
0

You can put the code blow into a function.

Set myRange = Workbooks(KGRReport).Worksheets(spreadSheetName).Range("B2:H" & lastrowinSpreadSheet).SpecialCells(xlVisible)

In the function, use on error goto xxxx. When error return nothing from the function and use "if myRange is not nothing then" to ignore the error cells.

Currin answered 12/4, 2016 at 2:37 Comment(1)
This would be clearer if it included a code sample to produce the behavior you're describing. I believe it will work, but it may not be obvious how to implement the suggestion.Fleshly
N
0

Neither of the responses below worked for me. Here is what I finally found that worked:

Sub fileterissues()

Dim VisibleRows as Long

‘Some code here

With Sheets(ws1).Range(“myrange”)
.Autofilter Field:=myfieldcolumn, criteria:=myfiltercriteria
VisibleRows = Application.Worksheetfunction.Subtotal(103, sheets(1).mycolumnfieldrange)
If VisibleRows = 0 then Resume Next
End with

‘More code

End sub
Nimocks answered 10/8, 2018 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.