13

I have a dynamically defined named range in my excel ss that grabs data out of a table based on a start date and an end date like this

=OFFSET(Time!$A$1,IFERROR(MATCH(Date_Range_Start,AllDates,0)-1,MATCH(Date_Range_Start,AllDates)),1,MATCH(Date_Range_End,AllDates)-IFERROR(MATCH(Date_Range_Start,AllDates,0)-1,MATCH(Date_Range_Start,AllDates)),4)

But if the date range has no data in the table, the range doesn't exists (or something, idk). How can I write code in VBA to test if this range exists or not?

I have tried something like

If Not Range("DateRangeData") Is Nothing Then

but I get "Runtime error 1004, method 'Range' of object '_Global' failed."

Colten J Nye
  • 165
  • 1
  • 1
  • 6

5 Answers5

38

Here is a function I knocked up to return whether a named range exists. It might help you out.

Function RangeExists(R As String) As Boolean
    Dim Test As Range
    On Error Resume Next
    Set Test = ActiveSheet.Range(R)
    RangeExists = Err.Number = 0
End Function
Steztric
  • 2,832
  • 2
  • 24
  • 43
  • Shouldn't you have an `On Error Goto 0` before `End Function`? – Archimedes Trajano Sep 13 '16 at 17:42
  • 5
    Hi Archimedes. According to the documentation here https://msdn.microsoft.com/en-us/library/aa266173(v=vs.60).aspx each procedure has its own independent error handler. When control passes back to the parent the previous error handler should bcome active again. It is a little ambiguous though, so put it in if it makes you feel safer. – Steztric Sep 13 '16 at 18:39
  • What does `RangeExists = Err.Number = 0` do? – Nicholas Apr 22 '17 at 02:02
  • @Nicholas there is an implicit Err object that is set whenever an error occurs. The second = operator is an equality comparison to see whether an error occurred or not. The first = operator is an assignment which sets the result to RangeExists. – Steztric Apr 22 '17 at 08:07
  • @Steztric I see. I wonder if `ActiveSheet` is necessary in `ActiveSheet.Range(R)`. If `R` is a string that specifies the range in another sheet (e.g., using RefEdit), this will be rejected even though `R` is a legal range. Also, I think maybe it's better to set `Err.Number` back to 0 before ending this function so the scope of the error only exists inside the function `RangeExists`, not outside, say if you call it in some subprocedure. – Nicholas Apr 22 '17 at 15:21
  • 1
    I had to change the last line in the function to `RangeExists = (Err.Number=0)` to return the correct boolean value. Else it would always return `false`. Using Excel 365. – Piemol Jun 23 '19 at 09:26
  • 1
    Alternatively I use `Set Test = ThisWorkbook.Names(R).RefersToRange`. This works for all named ranges in the entire workbook. Note that named ranges with worksheet scope (not workbook scope) must be written with preceding sheet name like `R = "SheetName!RangeName"` in that case; named ranges with workbook scope (default for named ranges) must be written without preceding sheet name. – martin.lindenlauf Sep 24 '19 at 09:34
18

You can replicate the match in your VBA to count before using the range how many rows you would have, or you can use error handling:

On Error Resume Next

Debug.Print range("DateRangeData").Rows.Count

If Err = 1004 Then
    MsgBox "Range Empty"
    Exit Sub
Else
    MsgBox "Range full"
End If

Err.Clear
On Error GoTo 0
nutsch
  • 5,922
  • 2
  • 20
  • 35
3

This is another approach. It has the advantage to take the container and the name you want to test. That means you can test either Sheets Names or Workbook Names for example.

Like this:

If NamedRangeExists(ActiveSheet.Names, "Date") Then
    ...
Else
...
End If

or

If NamedRangeExists(ActiveWorkbook.Names, "Date") Then
   ...
Else
   ...
End If

Public Function NamedRangeExists(ByRef Container As Object, item As String) As Boolean


Dim obj As Object
Dim value As Variant

On Error GoTo NamedRangeExistsError:

    value = Container(item)
    If Not InStr(1, CStr(value), "#REF!") > 0 Then
        NamedRangeExists = True
    End If
    Exit Function

Exit Function

NamedRangeExistsError:
    NamedRangeExists = False
End Function
codea
  • 1,439
  • 1
  • 17
  • 31
1

Depending on the application you're doing, it's good to consider using a Dictionary. They're especially useful when you wanna check whether something exists. Take this example:

Dim dictNames as Scripting.Dictionary

Sub CheckRangeWithDictionary()

    Dim nm As Name

    'Initially, check whether names dictionary has already been created
    If Not dictNames Is Nothing Then
        'if so, dictNames is set to nothing
        Set dictNames = Nothing
    End If

    'Set to new dictionary and set compare mode to text
    Set dictNames = New Scripting.Dictionary
    dictNames.CompareMode = TextCompare

    'For each Named Range
    For Each nm In ThisWorkbook.Names
        'Check if it refers to an existing cell (bad references point to "#REF!" errors)
        If Not (Strings.Right(nm.RefersTo, 5) = "#REF!") Then
            'Only in that case, create a Dictionary entry
            'The key will be the name of the range and the item will be the address, worksheet included
            dictNames(nm.Name) = nm.RefersTo
        End If
    Next

    'You now have a dictionary of valid named ranges that can be checked

End Sub

Within your main procedure, all you need to do is do an existence check before using the range

Sub CopyRange_MyRange()

    CheckRangeWithDictionary

    If dictNames.exists("MyRange") then
        Sheets(1).Range("MyRange").Copy
    end if

End Sub

While loading the dictionary may look a little longer, it's extremely fast to process and search. It also becomes much simpler to check whether any named range referring to a valid address exists, without using error handlers in this simple application.

Please note that when using names at sheet level rather than workbook level, it is necessary to use more elaborate keys to guarantee uniqueness. From the way the dictionary was created, if a key is repeated, the item value is overwritten. That can be avoided by using the same Exists method as a check in the key creation statement. If you need a good reference on how to use dictionaries, use this one.

Good luck!

FCastro
  • 581
  • 6
  • 7
0

This is an old post, but none of the rated answers has a dynamic solution to test if a name exists in a workbook or worksheet. This function below will accomplish that:

Function pg_Any_Name(thename As String) As Boolean
Dim n As Name, t As String
   
   For Each n In ThisWorkbook.Names
      t = Mid(n.Name, InStr(1, n.Name, "!", vbTextCompare) + 1, 999)

      If UCase(thename) = UCase(t) Then
         pg_Any_Name = True
         Exit Function
      End If
   Next n

End Function

Worth noting that this would not have worked for this specific question because OP had a dynamic defined range. This question would have been more accurately titled Test if Name is a Valid Range because the name always existed as a formula, the issue was if it was a valid RANGE. To address this question with a solution that checks both workbook and sheets... this function would work:

Function PG_Range_Name(thename As String) As Boolean
Dim n As Name, t As String
  
   For Each n In ThisWorkbook.Names
      t = Mid(n.Name, InStr(1, n.Name, "!", vbTextCompare) + 1, 999)
      
      If UCase(thename) = UCase(t) Then
         On Error Resume Next
         PG_Range_Name = n.RefersToRange.Columns.Count > 0
         Exit Function
      End If
   Next n

End Function
pgSystemTester
  • 8,979
  • 2
  • 23
  • 49