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)
Related
I am creating a report request form in Excel that essentially forces the requester to go step by step within the document to ensure we have everything filled out. As a control, I'm looking to lock the inputs in the further steps until the current step has been completed (i.e. simply putting text in a cell). I've put names in the Name Manager in Excel for each of the input steps.
For the life of me I can't get my code to work. I've even just tried to implement a MsgBox to make sure the IF is working correctly, but nothing appears.
Name Manager References:
Cell Reference IsRequestDetailsFilled = Cell O2. This cell has formula where it's logic will simply state "Yes" or "No".
Cell Reference BusinessNeed = Merged Cells B13:F16
The objective is for the macro to refer to IsRequestDetailsFilled. If it is "No", then lock BusinessNeed. If it is "Yes", then unlock BusinessNeed.
My code:
Private Sub Is_RequiredRequesterDetails_Filled()
'Dim CurrentWorksheet As Worksheet
Dim IsRequestDetailsFilled As Range
Dim BusinessNeed As Range
'Set CurrentWorksheet = Worksheets("New Report Request")
Set IsRequestDetailsFilled = Range("IsRequestDetailsFilled").Value
Set BusinessNeed = Range("BusinessNeed").Value
If IsRequestDetailsFilled = "No" Then
MsgBox Prompt:="Locked"
'CurrentWorksheet.BusinessNeed.Locked = True
Else
MsgBox Prompt:="Unlocked"
'CurrentWorksheet.BusinessNeed.Locked = False
End If
End Sub
Maybe I'm going insane because I haven't stopped working since 6 AM and it's now 10:30 PM... but I can't seem to find this answer from Googling it. I've tried several alternatives. I want the code to be readable, so ideally I would like to use the Reference Names I've designated.
If there's a better way to achieve what I am saying, please feel free to suggest that too.
Thanks!
Consider the logic of this excerpt from your code.
Dim IsRequestDetailsFilled As Range
Set IsRequestDetailsFilled = Range("IsRequestDetailsFilled").Value
The value property of Range("IsRequestDetailsFilled") is a variant - maybe a number or a string - but IsRequestDetailsFilled is a range. The appearance is that you are conflating the range itself with its value. The value property is just one of the many properties every range has, like Row, Column, Font, Fill etc. It so happens that the Value property is the default and therefore can be omitted which makes it the source of countless miscomprehensions.
Below is the correct syntax for assigning a range object to a variable declared as range.
Dim IsRequestDetailsFilled As Range
Set IsRequestDetailsFilled = Range("IsRequestDetailsFilled")
In practice you may not need that. The following code will work just as well and is perhaps easier to read.
Private Sub Is_RequiredRequesterDetails_Filled()
If Range("IsRequestDetailsFilled").Value = "No" Then
MsgBox Prompt:="Locked"
'CurrentWorksheet.BusinessNeed.Locked = True
Else
MsgBox Prompt:="Unlocked"
'CurrentWorksheet.BusinessNeed.Locked = False
End If
End Sub
If Range("IsRequestDetailsFilled") is declared with a scope for the worksheet only the worksheet must be specified. By default, Excel will create named ranges with a workbook-wide scope. You can check and set the scope in the Name Manager. If the name was created with a workbook-wide scope VBA will find it anywhere in the workbook and you don't need to mention the tab in the code.
I'd think that the following code should produce a diagonal of numbers, and I am wondering why this code doesn't work as intended:
Sub RangeExample()
Dim a As Range
Set a = Sheets(1).Range("a1")
a.Value = 0
Dim i As Integer
For i = 1 To 100
a = a.Offset(1, 1)
a.Value = i
Next i
End Sub
I understand there are many ways of producing a diagonal of numbers, I'm not asking how to do that.
What I'm asking is how I would change my range variable a to become a different range, and do that iteratively. It seems to me that as a.offset(1,1) returns a range object that's one over and one down, I should be able to reassign a as this new range, assign a value, and move on.
Your current issue is that you're missing a Set:
Set a = a.Offset(1, 1)
Note that you could also just use i and not reSet:
a.Offset(i, i).Value = i
Another option is to use Cells, e.g.
Sheets(1).Cells(i + 1, i + 1).Value = i
There's more than one way to skin a cat - pick whatever is easiest and most intuitive to future you.
Thanks for the answer, I didn't know set was required in this case. The specific answer I was looking for I have now found at:
What does the keyword Set actually do in VBA?
Specifically, the following answer by LeppyR64. "Set is used for setting object references, as opposed to assigning a value."
I didn't know that equality alone only impacted the value of the range object a. To actually change the range a was referencing, I needed set because a is supposed to refer to a new range object.
the issue has already been addressed by #BigBen
but you could avoid re-setting the range at every iteration by means of With...End With block
Option Explicit
Sub RangeExample()
Dim i As Long
With Sheets(1).Range("a1") ' reference topleftmost cell
.Value = 0 ' write referenced cell value
For i = 1 To 100
.Offset(i, i).Value = i 'write referenced cell current offset value
Next
End With
End Sub
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!
I´m using a UDF that is basically a vlookup simplified. Here´s the code:
Function SUELDOBASICO(Columna As Integer) As Double
SUELDOBASICO = Application.WorksheetFunction.VLookup(Application.Caller.Parent.Cells(Application.Caller.Row, 3), Application.Caller.Parent.Parent.Sheets("Escalas Salariales").Range("A3:DJ23"), Columna, False)
End Function
I´ve noticed that sometimes when copying sheets(within the same workbook), I get a #VALUE error. If I "edit" the cell in Excel, changing nothing, just using F2 and Enter, the error disappears. It used to happen when simply changing windows (to Firefox, and back to Excel, for instance). That´s why I used Caller and Parent so much in the code. It is almost completely fixed, except when copying sheets sometimes. I can´t seem to find the source of the error.
Help please.
I know this isn't your exact question, but, if at all possible, I would suggest to just avoid VBA completely if that's at all an option and write your formula as follows:
=VLOOKUP(INDIRECT("C"&ROW()),'Escalas Salariales'!$A$3:$DJ$23,XXXXX,false)
and XXXXX can be the same as your Columna variable currently.
That would guarantee your code to work as needed.
Given what was discussed in the comments and trying my absolute best to ensure this works, I actually don't see anything wrong with your code and am just GUESSING it may have something to do with Application.Caller.
When this kind of thing happens to me, I try my best to just use the debugger to figure out why - That usually involves either Stop statements to be able to step into code and see what happened or Debug.Print Err.Description kind of messages.
Either way, I tried to break each part down, so, at the very least you can see where the issue comes from.
To do so, I re-worked your function (with some major overkill)....
Function SUELDOBASICO(Columna As Integer) As Double
On Error GoTo ErrorCheck
Dim CellRef As Range
Dim LookupRef As Range
Set CellRef = Cells(Application.Caller.Range("A1").Row, 3)
Set LookupRef = Application.Caller.Worksheet.Parent.Sheets("Escalas Salariales").Range("A3:DJ23")
SUELDOBASICO = Application.VLookup(CellRef, LookupRef, Columna, False)
Exit Function
ErrorCheck:
Stop
Resume
End Function
(Also note that I changed Application.WorksheetFunction.VLookup to Application.VLookup - Look at this link for an explanation)
Once you figure it out, I would, though, remove the error code from the function as that isn't a good idea for production code - Just for Debugging.
Hopefully that can give you the answers you are looking for.
Hope that helps....
UPDATE #2:
Taking into account the possibility that copying the sheet is causing this error, here's a test to see if the process gets fixed:
Function SUELDOBASICO(Columna As Integer) As Double
On Error GoTo ErrorCheck
Dim NumTimesErrored As Integer
Dim StartTime As Double
Dim WaitSeconds As Integer
NumTimesErrored = 0
Dim CellRef As Range
Dim LookupRef As Range
Set CellRef = Cells(Application.Caller.Range("A1").Row, 3)
Set LookupRef = Application.Caller.Worksheet.Parent.Sheets("Escalas Salariales").Range("A3:DJ23")
SUELDOBASICO = Application.VLookup(CellRef, LookupRef, Columna, False)
Exit Function
ErrorCheck:
' This will make it tries this "hack" up to 3 times:
If NumTimesErrored < 3 Then
StartTime = Now
WaitSeconds = 1 ' Wait one second
Loop While Now - TimeStart < TimeSerial(0, 0, WaitSeconds)
DoEvents ' Allows all the other processes to complete
Loop
' Increment the number of times you've tried this:
NumTimesErrored = NumTimesErrored + 1
' Go back to the calculation step that errored
Resume
End If
Stop
Resume
End Function
The is no need to use caller and parent.
Function SUELDOBASICO(Cell as Range, LookupRange as range, Columna As Integer) As Double
' When you call the function :
' set Cell to be the cell in column C in the same row
' Set LookupRange to Sheets("Escalas Salariales").Range("$A$3:$DJ$23")
SUELDOBASICO = Application.WorksheetFunction.VLookup(Cell, LookupRange, Columna, False)
End Function
example of formula in a cell...
=SUELDOBASICO(C10,'Escalas Salariales'!$A$3:$DJ$23)
I am trying to write a function and a procedure that calls on such a function to check if names of the items located in a large table of repetitive data appear on a different sheet that's basically sort of like an input sheet for a user or a set up sheet. Basically the code should catch misspellings and unmatched items in the table on Sheet2 vs. a range on Sheet1. Ideally I would want to add an ability for the code to stop on an unmatched item and maybe highlight it so that user could correct an error or research the issue further. Currently I've tried to write the first part of the code but something is not working. It always says item name doesn't exists no matter how i set up data in the table:
Option Explicit
Public Function ItemNameExists(Target As String) As Boolean
Dim lookUp As Excel.Range
Dim itemCell As Excel.Range
ItemNameExists = False
Set lookUp = Worksheets("Sheet2").Range("Table1[Items]")
For Each itemCell In lookUp
If (itemCell.Value = Target) Then
ItemNameExists = True
Exit Function
End If
Next
End Function
Sub CheckErrors()
Dim ItemName As Excel.Range
Dim Cell As Range
Set ItemName = Worksheets("Sheet1").Range("B3:B12")
For Each Cell In ItemName
If (Len(Cell.Value) > 0) Then
If (Not ItemNameExists(Cell.Value)) Then
MsgBox ("Item name doesn't exist on Sheet1")
Exit Sub
End If
End If
Next
End Sub
For searching a Range there is a specific method, Range.Find (official documentation here).
If no match is found, it returns Nothing.
As to your case, there are quite a few points where it may miss the target, so you would probably have to debug:
Make sure lookUp actually refers to the intended Range.
Make sure Target contains the intended text.
Try with itemCell.Text instead of itemCell.Value (if you still prefer not to use Range.Find).