Use Named Range in UDF - excel

I'm trying to create a UDF that will use a named range. My named range ("best_Grade") is a single cell, with a value. (The named range is scoped to the Workbook).
In a workbook module, when I try to create the variable using a named range, I get
Run-time error '1004': Method of 'Range' of object '_Global' failed
Neither line works:
Dim namedRng As Range
Dim locDataWS As Worksheet
Set locDataWS = Sheets("Approval matrix 16")
Set namedRng = Range("best_Grade") ' errors
Set namedRng = locDataWS.Range("best_Grade") ' When I take above line out, this line errors too
and I've tried:
Dim wb As Workbook
Set wb = ActiveWorkbook
Set namedRng = wb.Names("best_Grade").RefersToRange
How come it's erroring out? This page says it should be working, no? Do I have to put this UDF on the actual sheet object, and not in a workbook module?
Edit: Note: the named range is not set to a cell, but a SumIf formula (best_Grade = SumIf(A2:A10,"x",...)` which may be causing the error?
Edit2: Yeah, that's why I think. I created a named range for a random cell, and was able to use Range("a_grade").Value and it returned the expected value. Since my best_Grade range is a formula, I think that's why it's erroring out. However, I don't know why, as I'd think a named range is a named range, no matter what it's made up of...
Edit n+1: Note there are two "answers" to this. If I wanted to continue using a named range as a Range variable, see my answer below. However, what I really wanted to do was what #MacroMarc posted, so I chose that as the "Answer".

You need to use the Names collection instead:
Sub t5()
' named range "Test" is `=Sum($A$1:$A$4)`
Dim rng As Double
rng = Evaluate(Names("Test").Value)
Debug.Print rng
End Sub
There are various properties that Name objects have to return string representations.

Sub t5()
' named range "Test" is `=Sum($A$1:$A$4)`
Set wb = ActiveWorkbook
Dim rng As String
rng = wb.Names("Test").RefersTo
Debug.Print rng
rng = Application.Evaluate(wb.Names("Test").RefersTo)
Debug.Print rng
End Sub
After some testing I found the above worked...and kind of interesting. I got the lead from Macro Man's comment as well at Chip Pearson post.
The key is the defined names returns a string ="your result" so you can either evaluate it to get the answer, or you can do some string manipulation to pull off the quotes and equal sign. You really were close with your RefersToRange choice.
See Vegard's comment under your own posted answer.

The reason it wasn't working is because my named range best_Grades was not a cell reference, but a formula instead. Thus, when using Range("best_Grades").Value, it was erroring out. (best_Grades = SumIf(A2:A10,"x", B2:B10, ...)
Not sure why, since I'd think a named range is a named range, regardless of what makes that up...but I suppose not.
For now, my solution is just to create another named range, based on an actual cell value, and then use that. (theBest_Grades = A2). Then, I can call simply Range("theBest_Grades").Value without any issues.
I'll leave this open for a few days, in case someone has an idea of how I can keep my formula named range, and use that in VBA.
Edit: This was basically how I originally had the worksheet/named range:
with the named range being given as:
But, as I said, you can't use that type of named range in VBA (at least not that I have found).
So, to solve it, I just used that SumIf in the cell, and gave that cell the named range:
And now I can use Range("findWindow_Example").Value without issue.
Edit n+1:
I tried doing a simple test with a Double, same thing though, it errors out:
Sub t5()
' named range "Test" is `=Sum($A$1:$A$4)`
Dim rng As Double
rng = Range("Test") 'Run time error 1004
Debug.Print rng
End Sub

One thing I haven't seen mentioned here is that a ground rule for UDF's is broken here IMHO:
ALL information a UDF needs should be passed to the UDF through its arguments.
If you adhere to that rule, using any range name becomes simple, since the value of the range name will automagically be transferred to the argument.

The correct way to solve the problem has been found by more than one answerer, but as I said in the comments (and subsequently, thought might be of interest to others), the reason for the error wasn't specified yet.
The named range you defined does not return a range object. This means that this code:
Dim namedRng As Range
Set namedRng = Range("best_Grade")
couldn't possibly work (primarily because the named range returns a numerical value. If it returned a string address representation, it might have worked with some syntax improvements).
To illustrate this from the compiler's point of view, look at the print-outs in the immediate window here (the first line in particular):
If we assume the initial code to be pseudo-code, what was being asked of the compiler was to construct a range out of the formula (not its' result either!).
So if we swap Set namedRng = Range("best_Grade") for Set namedRng = Range(Names("namedRange")) the result might presumably (but not necessarily -- see end of post!) look like:
Set rng = Range("=SUMIF('Ark1'!$B$1:$B$5, "x", 'Ark1'!$A$1:$A$5)")
And of course, this would not work. But throwing namedRange into an Evaluate would, as the other answers demonstrate, be perfectly legal!
Interestingly, if we do ? Evaluate(Names("namedRange")) (omitting the .Value), we get an Error 2015, despite being able to ask the compiler ? Names("namedRange") and get a string in return!

Related

Why is Range.Find() method so confusing?

This code gives me a 424 error "Object Required".
Dim playerLocation As Range
Set playerLocation = Sheets("World Map").UsedRange.Find("&").Address
And this works fine
Set playerLocation = Sheets("World Map").UsedRange.Find("&")
But when i put them into the immediate window:
?Sheets("World Map").UsedRange.Find("&").Address
$T$18
?Sheets("World Map").UsedRange.Find("&")
&
Is it just me or this completely backwords? Doesn't a Range variable take a range object? But it creates an error when i explicitly give it the Address of a cell. I'm so confused.
From Microsoft, Range.Find method:
Return value:
A Range object that represents the first cell where that information is found.
However, the Range.Address property:
Returns a String value that represents the range reference in the language of the macro.
Perhaps one way of thinking about it, is that by using .Address explicitly, you're asking for a String. You're not asking for an Object (which Range) is. If you were, you'd correctly declare your object and just leave off .Address.
And of course, you can always get the Address property from a Range Variable...Dim findRng as Range // Set findRng = Worksheets(1).Cells.Find("something") // debug.print findRng.Address.
You can also access all other Range properties now, so when I personally need to use Find, I tend to set that as a Range, so I don't have to "work backwards" later if I want to, say bold the cell, etc.

Can I set a dynamic range in vba excel to use in filter

The following excel sub is a filter that is filtering out rows based on the rows in the criteria row.
The code works well when the ranges are set with absolute data. I want to change the code to take the range from references stored as cell values (an indirect reference) but I cannot find a way to adapt other code snippets I see to work and I wonder if anyone can help me. I am not a programmer.
The problem is that as new data is inserted from time to time the range reference will move and the start of the data an the associated filter is in cell (using RC notation 14,14) and the data in cell 13,12. While I know I can’t use the indirect function in vba I wondered if there is a way to dynamically assign a range to be able to use the Advance filter function.
I have the code to find the last column and row of the data block.
I have tried the following code (2 attempts) but it won’t let me use the object in this way
I have tried to crate the cell reference as a string then assign it using the range function. I then read an answer where someone had put the value of the cells directly into the range function and it has worked for them ( they were copying cells). The 2 attempt are broadly the same but in the second I am trying to be more specific.
The issue seems to be as soon as I change from an absolute reference "A50" in the range statement the range no longer works. I am unsure how to resolve this and perhaps it can't be
It may be helpful to know the that data being filtered is rows of name and telephone data along with a tally system to show attendance (one column per week for a year)
The cells with the dynamic data hold them in the form A1 not RC format
Sub UseAdvancedFilterInPlace()
'This version of the sub has absolute references and works perfectly
Dim rdData As Range
Dim rgcriteria As Range
Call TurnOffStuff
Set rgData = Sheet9.Range(“A50”).CurrentRegion
Set rgcriteria = Sheet9.Range(“A46”).CurrentRegion
rgData.AdvancedFilter xlFilterInPlace, rgcriteria
Call TurnOnStuff
End Sub
Sub UseAdvancedFilterInPlace()
'This version of the sub has dynamic references and fails
Dim rdData As Range
Dim rgcriteria As Range
Call TurnOffStuff
Dim Top_of_data As String
Dim Top_of_Criteria As String
Dim My_range As Range
‘Attempt 1
'Set rgData = Range(Sheet9.Cells(13, 12).Value).CurrentRegion
'Set rgcriteria = Range(Sheet9.Cells(14, 14).Value).CurrentRegion
'Attempt 2
Set rgData = Sheet9.Range(Sheet9.Range(Cells(13, 12)).Value).CurrentRegion
Set rgcriteria = Sheet9.Range(Sheet9.Range(Cells(14, 14)).Value).CurrentRegion
rgData.AdvancedFilter xlFilterInPlace, rgcriteria
Call TurnOnStuff
End Sub
The actual error message I get is an application-defined or object-defined error
This worked for me.
Set rdData = Sheet9.Range(Sheet9.Range("L13").Value).CurrentRegion
Set rgcriteria = Sheet9.Range(Sheet9.Range("N15").Value).CurrentRegion
given that Range("L13").Value is A50 and Range("N15").Value is A46.
extra: Use the statement Option Explicit in the first line of every module, out of every sub or function. This option throws an error on undeclared variables, and will help you avoid renameing mistakes on variables.

Clean up Named Range Addresses with Errors (e.g. #REF!)

I set up a named range, let's call him RngIn.
He has 3 cells, and his address refers to A1:A3
Next, I delete Row 2.
My RngIn now shows #REF! error (correctly) in its RefersTo property:
"=A1,Sheet1!#REF!,A2"
This means I cannot manipulate the rest of that named range using VBA, because of the Method 'Range' of Global Object error.
The range is created during a process, and if a user subsequently needs to delete one row for whatever reason, my future code will fail because it needs to know where the rest of the named range data is...
I have tried many ways to access the remaining address information for this range, in VBA, but failed so far, e.g.
Dim RngAddress As String
Dim RngIn As Range
Set RngIn = Range("A1:A3")
RngAddress = RngIn.Address
RngAddress = RngIn.RefersToRange.Address
RngAddress = RngIn.RefersTo
RngAddress = Replace(RngIn.Address, "Sheet1!#REF!", "")
What I ideally want to see in a text string as the result for RngIn is:
"=A1,A2"
Because A2 is now the location of the data which was originally in A3.
Not sure I understand this well: your example code does not use Defined Names (aka Named Ranges).
lets suppose you create a Name called RangeIn that refers to A1,A3,A5 and you then delete Row 3.
The RefersTo for RangeIn is now =Sheet1!$A$1,Sheet1!#REF!,Sheet1!$A$4
This code removes the Sheet1!#REF!, to leave the Name RangeIn referring to =Sheet1!$A$1,Sheet1!$A$4
Option Explicit
Option Compare Text
Sub ChangeRef()
Dim strAd As String
strAd = ThisWorkbook.Names("RangeIn").RefersTo
strAd = Replace(strAd, "Sheet1!#REF!,", "")
ThisWorkbook.Names("RangeIn").RefersTo = strAd
End Sub
In cases like this, I set the start and end points of my named ranges to be the cell above and the cell below the range where the user can delete, and then use the OFFSET or INDEX function to resize that range to exclude my bookmarks. Or I use Excel Tables, which can handle row deletions without returning #REF errors.

Using a variable for a range

I am writing a macro and am having issues with using a range variable. I think I am setting the range properly, but I cannot do anything with the range. Pseudocode below:
Dim rng As Range
Set rng = Report(1) 'A function which will be defined below
trackWkbk.Sheets(strFY).rng = 10 <--'THIS IS WHERE I am getting the error.
'trackWbkb is a workbook that is defined properly; I use it elsewhere with no errors.
'strFY is defined and working properly, I use it to call sheets in trackWbkb elsewhere with no errors.
The sub function code is:
Function Report(a) As Range
Select Case intMonth
Case intMonth = 7
Select Case a
Case a = 1
Set Report = Range("B2")
End Select
End Select
End Function
I know my select case statements are gonna get pretty convoluted (this is just a testing sample), but I don't think the issue is there.
Let me know if I should include more code. My code compiles. I think this is something simple that I am missing, but I have been looking online for the past half hour and can't seem to find anything that will resolve this. Does anyone know how to resolve this? Thanks
Your function already returns a Range object. It's not clear to me whether you're trying to obtain a range on another worksheet that is at the same address as a range on the active worksheet. However, you could directly obtain the desired range if you passed the worksheet reference to your function:
Function Report(ByVal poParentWorksheet As Excel.Worksheet, ByVal a As Integer) As Range
Select Case intMonth
Case intMonth = 7
Select Case a
Case a = 1
Set Report = poParentWorksheet.Range("B2")
End Select
End Select
End Function
Then you could use:
Set rng = Report(trackWkbk.Sheets(strFY), 1)
rng.Value = 10
When you set a range variable you are creating a pointer in memory to a specific range in a specific sheet. It's not storing a cell address, so you can't write
trackWkbk.Sheets(strFY).rng
Either adopt Excelosaurus's solution, or change your function to return a cell address as a string and then use
trackWkbk.Sheets(strFY).range(rng)

Excel: VBA referencing named range based on string variable

How do I reference/select a named range based on a string variable with the text being the name ranged name?
Example:
'Target is range variable which is set to cell that is double clicked.
'For this example lets say the cell value is "A1A"
Dim binName As String
binName = "B1" & Target(1).Value
MsgBox(binName) 'This displays "B1A1A" which is the name of the named range
Range(binName).Select 'I want to select the range with the name B1A1A
This gives me
Run-time error '1004':
Method 'Range' of object'_Worksheet' failed
on the last line of the above code.
I understand that Range() is looking for an object, but I dont know how to refer call a range name from a string variable.
Thank in advanced for any help.
Resolution Edit:
Per Davids recommendation below, I changed
Range(binName).Select
to
Application.Goto ThisWorkbook.Worksheets("B1").Range(binName) 'where "B1" is the worksheet name
It's possible to have a Workbook scoped range that is still explicitly assigned to a single worksheet, which I gather is what you want (Name refers to some range on some other worksheet, etc.).
If you're doing this as UI, rather than Select try using the following:
Application.GoTo Range(binName)
Or:
Application.GoTo [binName]
Tested, and seems to work even when a range is explicitly on another worksheet, Application.GoTo takes care of toggling to that sheet and selecting that range.
Application.Evaluate(binName).Select
Application Evaluate will evaluate a string and resolve the reference. It accepts strings as arguments, and doesnt need to resolve to a range (for example, Application.Evaluate("10") would result in a return of 10).
For more information, check out the MSDN documentation: https://msdn.microsoft.com/en-us/vba/excel-vba/articles/application-evaluate-method-excel.
EDIT: Worth noting, this isnt properly error handled, so it could potentially return a Nothing reference, or an error, if the input string is not a valid named range.
Additionally, ThisWorkbook.Sheets("SomeRangeName").Select should work (and does work) just fine. It is more likely that there is something wrong with the name of your range.

Resources