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)
Related
I am working on an Excel project and I wanted to implement a UDF to change the value of multiple cells. I tried the following code I found on another thread to change value on an adjacent cell, so I played a bit around with the Offset parameters:
Function clear_it()
Dim pas As String
pas = "Call del(" & Application.Caller.Offset(0 , 6).Address(False, False) & ")"
Evaluate pas
End Function
'---------------------------
Sub del(rng As Range)
rng = 0
End Sub
This code worked as intended and it "deleted" the cell which was 6 places to the right by setting its value to 0!
Following the same logic, I tried the following code to delete multiple cells on the same column:
Function clear_it()
Dim pas As String
Dim i As Integer
For i = 0 To 5
pas = "Call del(" & Application.Caller.Offset(0 + i, 6).Address(False, False) & ")"
Evaluate pas
Next i
End Function
'---------------------------------------
Sub del(rng As Range)
rng = 0
End Sub
However this code just crashed my Excel Workbook and it bricked it. The file was just restarting and crashing on an infinite loop and I could not access the code to change it. So I had to delete the file permanently.
To clarify my intentions:
I have a column of data. When I set a particular cell to 0, I want 5 cells beneath it to also become 0 automatically, without pressing a button or something like that, so I wanted to create something like an If formula 6 cells to the left which calls my function:
=IF(J6 = 0; clear_it();0)
To further clarify my attempts:
I have only tested it by putting the above condition on a single cell, hence an infinite loop by recursive 0 valued cells is not possible.
Can anyone perhaps help me on what the problem might be or if I am trying something I shouldn`t with the UDF behaviour? Or is my approach to this problem totally wrong and there is a simpler way to do this on Excel?
First time posting, apologies if I make any mistakes!
So, I'm having a pretty strange problem with my UDF. In my workbook, I have an invisible 'template' sheet named "Standard Phase Sheet", and a subroutine that a user can activate which copies that template sheet into a new, visible sheet that the user can then work with. There will be many copies of that template sheet throughout the workbook, but they will all have unique names.
My UDF is on that template sheet in several spots, and thus on every copy of the template sheet that a user makes. When working within one of these sheets, the UDF works just fine, and returns the values I'd expect.
However, when a user ADDS a new copy of the template sheet, SOMETIMES the UDF goes haywire and returns #VALUE errors in every place the UDF is being used.
Also, when a user DELETES one of the copies of the template sheet, the UDF ALWAYS goes haywire and returns #VALUE errors in every place the UDF is being used.
I'm not using ActiveSheet or anything like that, and I believe I'm correctly giving full references to the ranges I'm working with within the UDF. Any help will be appreciated, I'm in a bind here! Code for the UDF is below.
Also, because I'm sure I'll be asked the question, the neColumn variable within my code is a public variable that I use in several subroutines and UDFs. It is defined at the beginning of my module. Also, I am using Option Explicit at the beginning of my module as well.
Thank you!
Public Function fSum(ByVal Target As Range, bExtended As Boolean) As Single
'This function returns a sum, based on a range provided in the cell that holds the function.
'It checks to see if that line item has been marked as Non-Extended, based on the NE column
'that can be check marked. If that line item is marked NE, then only the NE sum columns can
'use that line item as part of their sum, and those values are removed from the E columns.
Dim sSum As Single
Dim i As Integer
Dim n As Integer
'This small section is used to determine complete references to the cell calling the function.
Dim sheetName As String
sheetName = Application.Caller.Parent.Name
'Loop through provided range, and sum up the contents based on whether they have been marked NE or not.
i = 1
n = Target.row
sSum = 0
If Sheets(sheetName).Visible = True Then
While i < Target.Rows.Count
If (bExtended = True) Then
If Sheets(sheetName).Range(neColumn.Address).Cells(n, 1) = vbNullString Then
sSum = sSum + Sheets(sheetName).Range(Target.Address).Cells(i, 1).Value
End If
Else
If Sheets(sheetName).Range(neColumn.Address).Cells(n, 1) <> vbNullString Then
sSum = sSum + Sheets(sheetName).Range(Target.Address).Cells(i, 1).Value
End If
End If
i = i + 1
n = n + 1
Wend
End If
fSum = sSum
End Function
Summarizing the comment thread in an answer for posterity:
I'm not sure why exactly you see this behavior.
There would be ways to better this UDF (including using Long instead of Integer, preferring a Do While...Loop to While...Wend, removing the .Visible check...
But in any case, it does feel like this is just replicating the functionality of SUMIFS so you might just consider going that route.
The reason is that your neColumn variable has become Nothing, because Excel is Volatile.
I assume that the start of your module looks something like this:
Option Explicit
Public neColumn As Range
Sub Auto_Open()
Set neColumn = Sheet1.Range("A1:B2")
End Sub
When you open the Workbook, you call the Auto_Open Sub to Set the neColumn variable. However - when certain actions occur, Excel rebuilds the VBA, which resets the Public Variables (such as neColumn) to their defaults (which, for an Object such as a Range, is Nothing). An easy way to trigger this is by deliberately throwing an error, such as attempting to run this:
Sub ThrowErr()
NotDefined = 1
End Sub
You can make it more visible to you by adding the following line to your fSum code:
If neColumn Is Nothing Then Stop
You either need a way to restore neColumn when it has been reset to Nothing, OR find a non-volatile way to store it.
I am assuming that this is not suitable to become a Const, because otherwise it already would be but you could turn it into a Named Range, or store the Address in a hidden worksheet / CustomDocumentProperty. These options would also allow you to store neColumn when the Workbook is saved for when you reopen it
I've ran into a problem with a processing tool I'm working on, namely with the initial hang time being close to 10 seconds long. The issue I'd identified is that when the data is originally Imported into the it proceeds to update every lookup array and function in the 5x1000 workspace. I've been thinking that a good way to speed up the processing was to limit the import to non-empty cells only but have been unable to get it done no matter what I tried.
By default this is the function that imports the data from the RadGridExport temp file into a buffer sheet for later processing:
Sub CopyData()
Dim rng As Range
Set rng = Workbooks("RadGridExport.xls").Sheets(1).Range("A1:E1000")
ThisWorkbook.Sheets(2).Range("A1").Resize(rng.Rows.Count, rng.Columns.Count).Cells.Value = rng.Cells.Value
End Sub
I have attempted to use IsEmpty(Cell.Value) method but that was less than effective
Sub CopyData()
Dim rng As Range
Set rng = Workbooks("RadGridExport.xls").Sheets(1).Range("A1:E1000")
For Each Cell In rng
If IsEmpty(Cell.Value) = False Then
ThisWorkbook.Sheets(2).Range("A1").Resize(rng.Rows.Count, rng.Columns.Count).Cells.Value = rng.Cells.Value
End If
Next Cell
End Sub
In fact it froze the entire workbook. That was the only way I'm familiar with that could have applied, so I'm in a bit of a dead end.
Is there a way to actually get my initial function to ignore empty cells? Or am I coming at this from the wrong way and there's some method for keeping the worksheet from doing a full update on every single cell, including those whose value doesn't change?
I have managed to find a solution to my problem.
First of all thank to #Pᴇʜ for an way to do dynamic ranges - I ended up not using it in this tool as a lot of processing relies on static cells in a range for auto calculations, but I have written it down for the future!
#JvdV 's suggestion worked like a charm, bringing the load time down from 31 seconds to 14 seconds average.
Current codes looks like this:
Sub CopyData()
Dim rng As Range
Set rng = Workbooks("RadGridExport.xls").Sheets(1).Range("A1:E1000").SpecialCells(2)
ThisWorkbook.Sheets(2).Range("A1").Resize(rng.Rows.Count, rng.Columns.Count).Cells.Value = rng.Cells.Value
End Sub
However it was still forcing a full refresh on all static values - so after digging deeper I realized that I was using a massively taxing clear gate in the main macro:
Sub clearAll()
Range("A:E").ClearContents
End Sub
Which caused that massive 14 second delay. After I changed that function to this:
Dim Workspace As Range
Set Workspace = ThisWorkbook.Sheets(2).Range("A1:E1000")
For Each Cell In Workspace
If IsEmpty(Cell.Value) = False Then
Cell.ClearContents
End If
Next Cell
The load time went down further from 14 seconds to under 2 seconds total.
Thanks again :)
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)
I have a For loop that iterates through each row, I need to pull the value from the D column for each row to use in the loop.
I was trying to use the following to pull the value by using the value of the counter as the row number.
X = Worksheets("Test").Cells(4, Counter).Value
I keep encountering a Type mismatch error #13
These are the two scenarios I can think of when you will get that error
A You have defined X as a specific data type but are assigning a different type to it. For example. You have defined X as Long but the cell contains a String. Let's say your cell is A1 and it has excelSU. To replicate the error see this example
Sub Sample()
Dim x As Long
x = ThisWorkbook.Sheets("Sheet1").Range("A1").Value
End Sub
Similarly you could have declared Counter of a specific type but using as a different. For example
Sub Sample()
Dim counter As Excel.Application
For counter = 1 To 20
x = ThisWorkbook.Sheets("Sheet1").Range("A" & counter).Value
Next
End Sub
B Let's again take the example of cell A1. Your cell has a formula error like #N/A or #DIV/0! or some other error. To replicate the error use the same code as above and you will get the Type Mismatch Error
EDIT
by using the value of the counter as the row number
BTW, Counter is not being used as a Row but as Column. The syntax is Cells(Row,Column)
You might be pulling strings into a Integer array, or something similar.
As for your for-loop :
I just answered someone's question on a somewhat similar problems.
I suggest going over the fully-functional code example I gave him.
Namely because:
You are using hard-coded values (4rth column). You will eventually run into maintenance problems. A user that adds a column will easily screw up your code.
Your code won't speak for itself. If you replace your hard-coded values with properly named variables, they will.
NOTE: This might be more consuming on the processor due to the number of intersections. You can always adapt your strategy if you ever come up with tables that have over 10,000 rows.
Check the link and answer, but meanwhile here's a short-version of how I'd do it with Tables / ListObjects:
dim listO as ListObject
set listO = Worksheets("Test").ListObjects(1)
dim listC as ListColumn
set listC = ListO.ListColumns("ColumnTitle")
dim listR as ListRow
dim anArrayX() as Variant
ReDim anArrayX (1 to listO.ListRows.Count)
dim intersectedRange as Range
for i = 1 to listO.ListRows.count
set rowRange = ListO.ListRows(i).Range
set intersectedRange = Application.Intersect(rowRange, listC.Range)
anArrayX(i) = intersectedRange.Value
Debug.Print anArrayX(i)
next i