Excel: VBA referencing named range based on string variable - excel

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.

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.

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.

Excel VBA Evaluate Function Wrong When Reference is Not Active Sheet for Named Range

EDITED WITH BETTER EXAMPLE
I'm trying to use the Evaluate function to evaluate a formula reference for a named range. However, when using the Evaluate function, if you do not explicitly state the sheet reference along with the cell reference, it will assume the active sheet as the cell reference. This causes the wrong result
In my real project I'm trying to only evaluate a part of the named range's formula, so it makes it even trickier.
Using a basic example of what I'm trying to do, let's say you have the following formula in Sheet 1 cell A1 whose name is MyCell:
="Don't evaluate this part"&"My Result Is " & A2
If the Active Sheet is Sheet 2 and you run the following code it will give you the wrong results (this is a quick and dirty example to illustrate the problem)
Dim s As String
s = Replace(Range("MyCell").Formula, """Don't evaluate this part""&", "")
Debug.Print Evaluate(s)
Instead of giving me the value that is in cell A2 of Sheet 1, it gives me the value that is in cell A2 of Sheet2.
Any ideas around this?
This is closest I found, but it is not my exact problem (despite similar titles) and it doesn't provide a solution:
Excel VBA evaluate formula from another sheet
The problem you are having is that by design Excel will assume all unspecific cell references are referring to the existing worksheet. This is why whenever possible it is recommended to explicitly state the worksheet in all code.
The cleanest way (verified with some MSDN definintion hunting) is to just explicitly state the worksheet without activating it:
Sub test2()
Debug.Print Range("MyCell").Worksheet.Evaluate(Range("MyCell").Formula)
End Sub
Alternatively this code will change the active worksheet to the correct one and then change it back after evaluation. Not recommended to perform sheet activations like the code below without extenuating circumstances. Not even here.
Sub test()
Dim ws As Worksheet
Set ws = ActiveSheet
Dim s As String
s = Replace(Range("MyCell").Formula, """Don't evaluate this part""&", "")
Range("MyCell").Worksheet.Activate ' Don't remember if .Worksheet or .Parent ??
Debug.Print Evaluate(s)
ws.Activate
End Sub
As pointed out in the comments by ThunderFrame, it is important to remember that this code assumes MyCell is a simple cell reference as stated in the question. Otherwise you will need to use other methods to determine the target worksheet name (or hardcode it).
Its nearly always better to use Worksheet.Evaluate rather than the default Application.Evaluate: as Mark Balhoff points out that allows you to control unqualified references.
But Worksheet.Evaluate is also usually twice as fast as Application.Evaluate.
See my blog post here for details
https://fastexcel.wordpress.com/2011/11/02/evaluate-functions-and-formulas-fun-how-to-make-excels-evaluate-method-twice-as-fast/
Your line:
Debug.Print Evaluate(Range("MyCell").Formula)
is equivalent to:
Debug.Print Evaluate("=""My Result Is "" & A2")
which is why you get results according to the value of A2 in the ActiveSheet.
If you want to inspect the contents of the formula, you can use this line:
Debug.Print [MyCell].Formula
If you want the value of MyCell with respect to Sheet1, then you have 2 options:
1 - Use Debug.Print Range("Sheet1!MyCell").Value
2 - Use Debug.Print Sheet1.Range("MyCell").Value

Use Named Range in UDF

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!

Excel 2007 Using the 'Find' function for a user-defined string, then copying the entire row for each occurrence to another sheet

I'm new to Excel VBA, and this is my first attempt to make a useful macro to speed up my work process.
What I'm trying to do
I have a data file of abut 15 thousand rows by about 15 columns. What I would like my macro to do is once I hit a button on a separate sheet, the code takes a string I have typed into a specific cell on that sheet, goes to the sheet with all of the data on, then uses the find function on one of the columns to find all instances of the string which I have defined.
Once all of the instances of the string have been located, I want to copy the corresponding rows and paste them into the sheet I ran the macro from.
To clarify, the column I want to locate the string in contains descriptions typed by people - there isn't just one word to look at; that is why I have been trying to use the Find function.
My attempt so far:
Sub FindTextBasicData()
'Define variables
Dim sourceSht As Worksheet
Dim outputSht As Worksheet
Dim strSearch As String
Dim searchRange As Range
Dim outputRange As Range
'Set the sheet variables to those present in the workbook
Set sourceSht = Sheets("Basic Data")
Set outputSht = Sheets("Output")
'Set the value of the string variable to the contents of cell C2 in the output sheet
strSearch = outputSht.Range("C2")
'Set the range variable to the range in the data sheet that I want to check
Set searchRange = sourceSht.Range("C6:C15448")
'Use the Find function to look through the range I defined and select all rows where the
'string variable can be found, setting the second range variable to these values
Set outputRange =searchRange.Find(What:=strSearch, After:=.Cells(3, 6), LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False).EntireRow
'Copy the results of the Find function to the clipboard
outputRange.Copy
'Select the 5th row of the output sheet as the location to paste the data, and then paste
outputSht.Select
Rows(5).Select
ActiveSheet.Paste
End Sub
I know that I'm definitely doing something wrong with the find function, but I can't figure the thing out - I think my problem lies with the After parameter in that it doesn't do what I think it does (references cell C6 as the place to start using Find from?). I tried looking at the guide to the Find function on Ozgrid, but I think I just confused myself more.
If I can get this macro to work correctly I will be able to use it a lot to vastly streamline the analysis of data that I have to do for these 15000 records. Any help for this would be greatly appreciated, and I'm of course happy to clarify if I haven't explained something well enough.
The reference .Cells(3, 6) needs to be qualified using a With block or just directly refer to a Worksheet or Range object. Easiest solution here would be sourceSht.Cells...
Also, Cells(3, 6) is cell F3 whereas you want cell C6. Put these together and you should therefore have After:=sourceSht.Cells(6, 3)
As mentioned above. You use dot operator in front of .Cells(3, 6) without With. The best way is to reference it to concrete sheet directly sourceSht in your case. If you want to reference to cell C6 then you can use for example :
sourceSht.Range("C6") or sourceSht.Cells(6,3) or sourceSht.Cells(3,"C") etc..
But I think that shouldnt cause problem (provided the reference is valid) because After parameter is not relevant (and optional) if all you want to do is search in the whole range. In fact only What is required parameter.
set outputRange = searchRange.Find(strSearch).EntireRow should do the trick. Moreover if you specify After parameter, the search doesnt look into that cell.
Anyway, this only gives you the first cell in the row in which the string was found. Not all of them. You might want to put the search code then into a cycle in combination with FindNext method or just using the After parameter of Find method.
Instead of the Range.Find method, use the Range.AutoFilter method to filter the rows on the first sheet based on the value on the second sheet, then copy only the visible rows. IMHO, this is better than the Range.Find method (which is still better than looping).
There are numerous examples on this site about how to copy visible rows to another sheet, here is one example: Excel Macros - Copy and paste filtered rows

Resources