Excel 2013 upgrade "broke" my VBA function - vba

The attached code is meant as a table of contents builder. Depending on what row the function is on, it plucks certain cells from subsequent worksheets.
Asset Location Model
__________________________________________________________
Freezer Kitchen Freezerator 5000 (from worksheet 2)
Television Den Panasung 55" (from worksheet 3, etc.)
Before the "upgrade", the function just worked. Now, it works if I edit sells, but if I copy cells within the same worksheet (anywhere, not just the "special" cells), the cells flicker and flicker for many seconds and then resolve. Sometimes it fails with an overflow error (if I recall correctly, it didn't do it today.) Copying cells from one worksheet to another just works with no such delay.
If I comment out the Application.Volatile line, the delays go away, but the function does nothing.
Code:
'Return values in subsequent worksheets based on row that the function
'is located on. Usage in this particular instance is a TOC
'Num = number of rows to offset, based on starting position of the TOC
'and skipping any worksheets not to be included in the TOC
'srow and scol is the target cell on the following worksheets
Function GetSpot(num, srow, scol)
Application.Volatile
If num > (ThisWorkbook.Worksheets.Count) Then
GetSpot = " "
Else
GetSpot = ThisWorkbook.Worksheets(num).Cells(srow, scol).Value
End If
If GetSpot = 0 Then GetSpot = " "
End Function
Any ideas?
Thanks in advance.

The issue you're experiencing is a bug in Office 2013 related to user-defined function use within a worksheet. We reported this to Microsoft and were told it will not be made part of a hotfix release as only 3 other people have reported it.
When you have a user-defined function within your worksheet and you perform an action such as copy/paste or insert rows, the worksheet takes painstakingly long to complete calculation. This occurs anytime you have a selection where there are marching ants (for lack of a better term) around the selection and the worksheet with user-defined functions calculates.
It's not your function, it's Office 2013. Here's a quick way to replicate the issue:
Create a new blank workbook
Open Visual Basic Editor and add a new Module
Paste the following VBA in the module
Function TimesTwo(ver)
TimesTwo = ver * 2
End Function
On the workbook, paste the formula =INT(RAND()*8) into cell A1
In the range D1:F100, paste the formula =TimesTwo($A$1)
After configuring the workbook like the above, perform a copy and paste within that worksheet.
The result is that the calculation will take a very long time to complete, which can be seen in the status bar.
There are no workarounds aside from using native Excel functions, if available or working with the Workbook Calculation set to Manual. That is, disabling calculation of the worksheet until you manually invoke it.

Related

Range SpecialCells ClearContents clears whole sheet instead

I have a sheet in Excel 2010 which is setup as a pseudo form (I didn't create it, I'm just trying to fix it) so formatting suggests that the user can only enter in certain cells. Depending on certain functionality these areas need to be reset, i.e. cleared although formulae and standard/conditional formatting need to be kept. I have defined each of these cells/ranges as named ranges so I can easily loop through them using the following code: -
Public Sub ResetDetailSheet()
Dim nm As Name
With ThisWorkbook
For Each nm In .Names
If Left(nm.Name, 9) = "nmrDetail" Then
Range(nm.Name).SpecialCells(xlCellTypeConstants).ClearContents
End If
Next
End With
End Sub
For some reason instead of clearing the constants from the specific range it is clearing constants from the entire sheet so I am losing all titles/headings. Formulae and standard/conditional formatting are staying as expected.
What am I doing wrong?!?!
As a test using the immediate window I tried clearing a specific cell, e.g.
Range("G7").SpecialCells(xlCellTypeConstants).ClearContents
But this still cleared all constants from the entire sheet.
What am I missing? I don't understand. Maybe I'm being dumb.
Sorry, I can't upload an example. This place is pretty locked down.
Range({any single cell}).SpecialCells({whatever}) seems to work off the entire sheet.
Range({more than one cell}).SpecialCells({whatever}) seems to work off the specified cells.
So, make sure your range has more than a single cell before you clear it - if the range is only a single cell, then check if it .HasFormula; if that's the case then its .Value isn't a constant:
With ThisWorkbook
For Each nm In .Names
If Left(nm.Name, 9) = "nmrDetail" Then
If nm.RefersToRange.Count > 1 Then
nm.RefersToRange.SpecialCells(xlCellTypeConstants).ClearContents
ElseIf Not nm.RefersToRange.HasFormula Then
nm.RefersToRange.ClearContents
End If
End If
Next
End With
Note that I'm using Name.RefersToRange instead of fetching the range by name off the active sheet.

Excel VBA: Insheet function code can not access other cells on sheet

I'm having some issues with an insheet function that I am writing in VBA for Excel. What I eventually am trying to achieve is an excel function which is called from within a cell on your worksheet, that outputs a range of data points underneath the cell from which it is called (like the excel function =BDP() of financial data provider Bloomberg). I cannot specify the output range beforehand because I don't know how many data points it is going to output.
The issue seems to be that excel does not allow you to edit cells on a sheet from within a function, apart from the cell from which the function is called.
I have created a simple program to isolate the problem, for the sake of this question.
The following function, when called from within an excel sheet via =test(10), should produce a list of integers from 1 to 10 underneath the cell from which it is called.
Function test(number As Integer)
For i = 1 To number
Application.Caller.Offset(i, 0) = i
Next i
End Function
The code is very simple, yet nothing happens on the worksheet from which this formula is called (except a #Value error sometimes). I have tried several other specifications of the code, like for instance:
Function test(number As Integer)
Dim tempRange As Range
Set tempRange = Worksheets("Sheet1").Range(Application.Caller.Address)
For i = 1 To number
tempRange.Offset(i, 0) = i
Next i
End Function
Strangely enough, in this last piece of code, the command "debug.print tempRange.address" does print out the address from which the function is called.
The problem seems to be updating values on the worksheet from within an insheet function. Could anybody please give some guidance as to whether it is possible to achieve this via a different method?
Thanks a lot, J
User defined functions are only allowed to alter the values of the cells they are entered into, because Excel's calculation method is built on that assumption.
Methods of bypassing this limitation usually involve scary things like caching the results and locations you want to change and then rewriting them in an after calculate event, whilst taking care of any possible circularity or infinite loops.
The simplest solution is to enter a multi-cell array formula into more cells than you will ever need.
But if you really need to do this I would recommend looking at Govert's Excel DNA which has some array resizer function.
Resizing Excel UDF results
Consider:
Public Function test(number As Integer)
Dim i As Long, ary()
ReDim ary(1 To number, 1 To 1)
For i = 1 To number
ary(i, 1) = i
Next i
test = ary
End Function
Select a block of cells (in this case from C1 through C10), and array enter:
=test(10)
Array formulas must be entered with Ctrl + Shift + Enter rather than just the Enter key.

Removing links from copied worksheet

What I want to do
I want a code in my workbook (wbDestination) that opens another workbook (wbBosOriginal) and copies an entire sheet as values (wbBosOriginal has a lot of code in it, in modules and in the worksheet in question, and I do not want this code because it references stuff in wbB that doesn't exist in wbDestination). I have had great problems pasting as values, because it will not paste columns and rows that are currently hidden. So this is why I decided to import the whole sheet instead.
What I tried and what's wrong with it
Here is a block of code I used to copy the worksheet in the destination workbook, in the last index position. The problem with it is that some links still exist to the old workbook (Formulas, validation lists, conditionnal formatting). I have deleted all these links but STILL when I paste the sheet successfully, save and reopen, I have an error saying some content is unreadable. I believe there are still some elements linked to the old workbook.
Set wbBosOriginal = Workbooks.Open(strChosenPath, ReadOnly:=True)
With wbBosOriginal.Sheets("BOS")
.Visible = True
'Pastes the ws in last position in wbDestination
.Copy after:=wbDestination.Sheets(wbDestination.Worksheets.Count)
Set wsNewBos = Worksheets(Worksheets.Count)
'Deletes VBA code in the copied sheet
ThisWorkbook.VBProject.VBComponents.Item(wsNewBos.CodeName).CodeModule.DeleteLines 1, _
ThisWorkbook.VBProject.VBComponents.Item(wsNewBos.CodeName).CodeModule.CountOfLines
End With
The worksheet is successfully pasted with no code in it, with everything else it had previously. I then remove all formulas, conditionnal formatting, and validation lists. Even after removing those as well, I still get an error when opening the workbook.
My question
Apart from conditional formatting, validation lists, VBA code, and formulas linking a worksheet that was pasted to a new workbook, what other elements could cause the workbook from opening in repair mode every time due to existing links to the old workbook?
If my question is not clear, comment and I will clarify.
Dealing directly with VBE seems a bit heavy-handed to me. If your code is manipulating several workbooks, I would put the code in an add-in and not have it in any workbook. (Technically *.xlam addins are workbooks, but when I say "workbook" I mean normal *.xls, *.xlsx, *.xlsm, etc.)
That said, if you're just copying cell values (which may be formulas) between different workbooks, you shouldn't have any dependencies other than cell references, named ranges, and user-defined functions in the original workbook. I would make sure there are none of those. Please also share how you are ensuring your formulas do not have broken references.
If the issue you are having is caused by trying to avoid hidden columns and rows not allowing pastevalues, why not unhide the rows and columns and then copy only the values to the new book?
Just cycle through each of the sheets in the original book and use the method .UsedRange.Hidden = False. As far as I am aware, this should unhide every cell on the sheet and allow you to do the original pastevalues calls
This works fast and smooth (it's harder to delete ALL the data Imo):
Sub tests()
Dim AllRange As Range: Set AllRange = ActiveSheet.UsedRange
Dim ItemRange As Range
Dim myWbDestination As Workbook: Set myWbDestination = ThisWorkbook
Dim SheetDestination As String: SheetDestination = ("Sheet2")
For Each ItemRange In AllRange
With myWbDestination.Sheets(SheetDestination)
.Range(ItemRange.Address) = ItemRange.Value
End With
Next ItemRange
End Sub
Repair mode can be triggered by many factors, you would need to post the code you are getting to look for an explanation, it would be like asking why vba may broke

UDF's on different sheets calling eachother return error 2015

I have 2 sheets with 3 UDF's in the first and 2 in the second.
sheet 1 is a monthly matrix with 1 column for each day where people put in their hours on the rows beneath. On 3 specific rows there are UDF's that consolidate the data in the column above, referencing the row as an argument. I do the function call like below to avoid having to make the UDF volatile (which prolongs calculation time greatly if I do), so the UDF's result updates when anything changes in column R:
calculateOvertime(R:R)
On sheet 2, the days of the month are in rows (not columns) where one can put in details about their day IF they did overtime. This is detected by one of the UDF's in sheet 1, so the 2 UDF's here require data calculated by a UDF in sheet 1
I have some strange issues with this setup:
For some reason, when I switch tabs, all cells containing any of these functions show up as #VALUE!. I have to add "Application.CalculateFull" to an event handler that fires whenever the tab is activated:
Private Sub Worksheet_activate()
Application.CalculateFull
End Sub
The UDF's that reference a cell containing another UDF on the other tab, will always get '2015!' as a value, referring to error 2015 (a Value error, because the cell contains #VALUE! when the sheet is not active)
Obviously these 2 issues are connected because when I shift sheets, the UDF-calculated values in the non-active sheet are somehow lost.
My method of getting a value from a cell is as follows. I figure out on which row the label in column A is by using the Find() function
Dim compensationRowIndex As Integer
compensationRowIndex = CInt(othersheet.Range("A1:A250").Find("COMPENSATION").Row)
then I get the value and cast it to a Single
Dim compensation As Single
compensation = CSng(othersheet.Cells(compensationRowIndex , columnIndex).Value)
the variable 'compensation' holds the value 2015 always.
Is there any way around this? Also when I want to print the sheets, all cells containing UDF's are filled with #VALUE!. My guess is : If I can make issue 1 go away, so will issue 2.
ANY help on this is much appreciated. I've been troubleshooting this for almost a whole day now and haven't found a solution googling the symptoms.
Problem Solved!
"ActiveSheet" inside a UDF doesn't mean "The Sheet the UDF-containing cell is on" It literally means "the sheet that's active".
When referencing a UDF on anoter sheet, things go horribly wrong. It was all a matter of replacing ActiveSheet with a variable that's set in an If statement that decides from where the UDF is called.
In my case the second sheet always has the word "Info" in it. When on that sheet, you should go one sheet to the left:
Dim ws As Worksheet
If InStr(ActiveSheet.Name, "Info") = 0 Then
Set ws = ActiveSheet
Else
Set ws = Worksheets(ActiveSheet.Index - 1)
End If
Not a 100% waterproof solution (e.g. what if someone reorders the sheets), but for my purposes it's close enough.
The following code snippet (used within a UDF) should do what you want:
Dim ws As Worksheet
If TypeOf Application.Caller Is Range Then
Set ws = Application.Caller.Parent
End If
The Caller property of the Application object points to the Range covering the cell(s) where the UDF was called from. ws will point to the containing worksheet then.
The If TypeOf clause avoids errors in case the function has been called by something else than a UDF (for instance, another VBA procedure), where Caller might not point to a Range object. In such a case, ws remains unassigned (Nothing).

VBA - Copy from Closed File paste to next available row on Summary Sheet

This is my first post. Forgive me if i am doing something wrong here. I will be glad to correct any mistakes. I have found the web site to be very valuable as i am a baby in the field of vba. Please have patience with me.
I am a super rookie in VBA. I am learning as i go but have spent a lot of time on this. I find bits and pieces of information on the web but have trouble in putting them all together. I have learned how to make a vba macro that i can select a file and then run other macros.
I am using Excel 2013.
I complete a time sheet every week(sometimes more at end of month) of the hours i work and the projects i work on. I also include on that sheet when i am out and a code for the reason. I would like to copy three sections to a summary sheet.
Cell D1. This cell always has the date beside it. I would like to copy this to the cell in my first row.
Cells F3-L3 are cells where a code is put. I would like to copy this to the second cell in my first row.
The next range of cells aret the last cells with data in columns F-L. These vary as we have different numbers of rows for work orders each time but are always in columns F-L. I would like to copy this to a second row below the corresponding cells in the first.
For the next file I would like to copy to the next available row in summary.
I would like to copy this data so i can figure vacation days, sick days, etc.
I know i'm asking alot but would be extremely grateful for any help.
i'm giving you this as Example, you will still need to modify...
Option Explicit 'forces Programmer to declare variables
Sub Button_To_Copy () 'link this to a button or other action that launchs the sub
Dim Range_to_Copy as Range
Dim Range_Destination as Range
Dim Sheet_Data as worksheet 'sheet from where we pull the data
Dim Sheet_Destination as Worksheet' Summary Sheet
set Sheet_Data = Thisworkbook.Sheets("Sheet1") 'you might have to rename the sheetname accordingly to its name.
set Sheet_Destination = Thisworkbook.sheets("Summary") ' wild guess, correct to your summary sheet name
Set Range_to_Copy = Sheet_Data.Range("D1")
Set Range_Destination = Sheet_Destination.range("A1")
Range_to_Copy.Copy Range_Destination 'this copies from range A to B (basically A.copy B), but i changed variable names to make it easier...
'more code, copies
'you can simplify without variables like this:
'Sheets("Sheet1").Range("D1").Copy Sheets("Summary).Range("A1") <===== does the same as the above coding
End Sub
Note that i never used activate or select, wich macro recorder will badly use all the time, making bad habits to starters.
Also, by referencing hard cell location like "D1", the code is not dynamic, if you add more data, the sub will have to be changed, so use this just as a start maybe