Why does the same code (Sheets.Add) exhibit two different behaviours? - excel

Earlier this morning I asked my first question. Whilst investigating the suggestion put forward by Marco, I encountered a "does not make sense" situation. I went back to the immediately previous version of the macro suite to see what behaviour that gave. Let's call these Suite A and Suite B
In both Suite A and B there is the following code:
Public wsTemp As Worksheet
Sub DrillDown()
In Suite A there is the following:
Sub HandleNewFileNumber()
If iK <> 0 Then
wbTgt.Close
End If
Set wsTemp = ThisWorkbook.Sheets.Add(After:=Worksheets(1))
In Suite B there is:
Sub HandleNewFileNumber()
If iK <> 0 Then
wbTgt.Close
End If
Set wsTemp = ThisWorkbook.Sheets.Add(After:=Worksheets(1))
No, I have not simply copied the same code twice. I took each piece of code out of the two separate workbooks. The code is identical.
When I run the macro suite A everything runs through to successful completion. When I run macro suite B I get Error 1004. In both cases the Set of wsTemp is the first time wsTemp has been referenced.
I am at a loss to understand how the same code can give two radically different results.

Try
Set wsTemp = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Worksheets(1))
Or
With ThisWorkbook
Set wsTemp = .Sheets.Add(After:=.Worksheets(1))
End With
It's a very common problem: When you access an object from Excel (Sheet, Range, ...) and don't specify where it belongs to (eg a Worksheet belongs to a Workbook, a Range belongs to a Worksheet), VBA assumes you are referring to whatever is currently active (currently having the input focus).
If you write Worksheets(1), Excel will translate this to the first worksheet of the current active Workbook. When ThisWorkbook is not the active workbook, this will lead to the situation that you ask Excel to add a sheet to one workbook but put it after the sheet of another workbook - and that's not possible and will throw an error.
VBA beginners will often try to solve this by put activate-statements in the code, but that's the wrong attempt. There is no need to activate anything. Instead, just tell VBA exactly what you want. By writing ThisWorkbook.Worksheets(1), you specify that you mean the first sheet of ThisWorkbook (which is the WB where the code lives in). The second piece of code (using With) is just another way of writing it - but note that .Worksheet has a leading . that signals that we are accessing the sheet of the Workbook specified in the With-clause.
As a rule of thumb, never put anything in your code that let's VBA guess something.

Having spent an hour or two working through the entire suite of macros, applying the technique suggested by #Fun Thomas, and tidying up some other loose code as I went along, I can no longer reproduce the failure on my most up-to-date version of the macros.
Thank you everybody for your thoughts and suggestions. I will now concentrate on offering further diagnostic data to my very original post about flickering grey screens when opening workbooks.

Related

Is it not a good Idea to use the Set Worksheets method multiple times during code execution?

I am attempting to copy different images to different worksheets of my Workbook using the following code below. I am changing the Target Worksheet Dynamically in a different sub, with a String Variable in the Global Declarations section. I can see the variable being passed to the sub and in fact it works the first pass through the code, but when I attempt to change the "TargetSheetIni" variable to a new sheet, it continues to use the first original sheet as it loops through.
Can you not change a target sheet after using the Set keyword? Should I refer to the sheet directly instead?
Sub Test1()
Dim TargetWS, SourceWS As Worksheet
Set TargetWS = Worksheets(TargetSheetIni)
Set SourceWS = Worksheets("Images")
DoEvents
SourceWS.Shapes(CurrentImageId).Copy
DoEvents
TargetWS.Paste Range(ColumnLetter2 & RwCnter)
DoEvents
End Sub
I think I may have figured it out. As far as I can tell the issue may be that I used the Copy Sheet Functionality in Excel when I originally created the target sheets. And even though I renamed the sheets both on the tab below and in the project editor... for some reason VBA kept targeting only the original sheet
I proved this by changing my code around to explicitly call the sheet I wanted to target like so:
ActiveWorkbook.Worksheets("Sheet2").Paste Range("I2")
And even doing that it would target sheet 1 for the paste command instead of the expected sheet 2. I deleted the three copy sheets and created a new one from scratch and re-executed code and now it targets sheet 2 as expected.
I found this article that sort of explains it I guess...
https://www.spreadsheetsmadeeasy.com/7-common-vba-mistakes-to-avoid/
Ok my last answer may have not been correct. It appears as though for some reason inserting an ws.activate caused my code to start workin.g
Very frustrating fix. as I have always heard to avoid using that.

How to copy a sheet and ensure it is at the end of all other sheets?

sheetCopy.Copy After:=ThisWorkbook.Worksheets(Worksheets.Count)
I use the code above to create a copy of a template worksheet in Excel.
Most of the time it creates the tab at the end which is what I want but sometime it creates new tab somewhere in the middle.
Is there a way to ensure it is copied to the end of the sheets?
(Thisworkbook.Worksheets.Count)
The issue
When working with a Workbook, Worksheet, Range, or other similar objects, it's best to avoid implicit member calls.
Most of these default to whatever is active. For instance, Worksheets.count is the same as ActiveWorkbook.Worksheets.count.
In your code you were almost there as you correctly used ThisWorkbook when accessing the Worksheets Collection; however, the Worksheets.count is defaulting to the ActiveWorkbook.
The Solution
To fix it, I like using With blocks to help shorten the code, and make it easier to be explicit in my refrences.
With ThisWorkbook
sheetCopy.Copy After:=.Worksheets(.Worksheets.Count)
End With

Sheet name does not exist when obviously it does

I have been developing a VBA, UserForm1 program for the past few weeks and I have performed numerous test and never once had this problem.
The code, shown below, is part of the Initialization routine. I made some enhancements to the program, far deeper in the code than the code under discussion. Now, I can't get past this point in the program. I replaced "Activate" with "Select", but this did not affect the outcome, i.e., an aborted run.
Please, can someone suggest what I am doing wrong? Or, how can a program that been tested dozens of times, suddenly develop a fault out of thin air?
'======================================================================
Dim WPA() As Variant 'Workbook path for Category Class
Dim WBA() As Variant 'Workbook name for Category Class
Dim WSA() As Variant 'Worksheet name for Category Class
'======================================================================
Dim WBK() As Workbook
..........
Set WBK(S) = Workbooks.Open(WPA(S))
Workbooks(WBA(S)).Worksheets(WSA(S)).Activate 'Activate Worksheet
NWS(S) = CheckSheetExists(WSA(S)) 'Check for Worksheet
..........
Function CheckSheetExists(SheetName) As Boolean
CheckSheetExists = Evaluate("ISREF('" & SheetName & "'!A1)")
If CheckSheetExists = False Then
MsgBox "Worksheet " & SheetName & " does not exist. Run aborted."
End '<==================Run aborts
End If
End Function
It looks like you are using global variables?
If so, you should be aware that they are not as permanent as you might expect.
There are a number of things which cause global variables to reset and become undefined.
See this answer for more info: https://stackoverflow.com/a/7043901/1473412
Using "End"
An unhandled runtime error
Editing code
Closing the workbook containing the VB project
Is it possible that the "enhancements to the program, far deeper in the code than the code under discussion", causes one of these things to happen, and so resets the global variables?
If you have to use global variables, you could store them safely in a worksheet. Or, everytime you try and use one, you could check that it is defined, and if not, redefine it?
That is a good question, Storaxs' answer should have fixed this though to my knowledge.
However when something like this happens I try to create a whole new workbook and paste all of this code into it (just for the sheet in question) and then copy all the cells data into the new sheets.
This has saved me once before and it may help you in this case as well.
I also recommend not deleting your old workbook in case you don't port everything over.
If this still doesn't work let us know and we will continue to look into this.

VBA Using Vlookup to assign Value to ActiveCell, in another referenced workbook

I've been trying to use vlookup in VBA and it's being quite annoying.
ActiveCell.Value = Application.WorksheetFunction.VLookup(Cells(currentrow, 12), currwor.Worksheets("Sheet1").Range("R4C3:R160C10"), 6, False)
is the code
The main elements I've used are
currwor which has been set to reference another workbook and I'm looking to use vlookup on the range in there.
However it keeps kicking up an error saying subscript out of range.
I suspect currwor is the guilty party in this
Set currwor = ActiveWorkbook (then the workbook in which the above is executed is opened, and that is assigned a workbook name as well.)
I can't use a specific range because I'm trying to perform the vlookup on each cell in a range, and that range changes everytime, and the currwor is to be executed on a particular document I get but the name may vary (format however remains the same.)
Any suggestions on it?
edit: One error is that it's not supposed to be sheet1 but 'Stock Report by Product'. However it's still kicking up the subscript out of range error.
by using Set currwor = ActiveWorkbook
you assign currently active workbook to currwor however you mentioned prior to that it should be a different workbook? meaning you should have a line Set currworb = Workbooks("desired workbook name")?
Yes - Idiotic mistake. As pointed out the sheet name was incorrect and the problem was that in execution I skipped the bit where the worksheet was assigned to currwor.
Idiotic enough that I should consider deleting this post off here.

Problem with adding event code to newly created sheet

I have a problem with adding an event code to a newly created sheet.
The problem seems to only occur right after I open the Excel workbook.
I use
Dim codemod As Object
codemod = ActiveWorkbook.VBProject.VBComponents(Worksheets("Sheet4").CodeName).CodeModule
to add the code to the created sheet module but when I try to run this code right after opening the Excel workbook it gives me an error: run-time error '9' Subscript out of range. The debug points to the codemod line.
The weird part is that this error does not come up again when I change the code just a tiny bit and then change it back to the original state. After I do this the code runs as it should i.e. inserts code to the newly created sheet.
Anyone got any idea what may be the problem?
There is not much else to the code except inserting the lines but that does not seem to be the problem.
Thanks in advance
(This is my first action on S.O. so please don't shout at me when I'm doing things wrong.)
Did you concider preparing a workbook + sheet with the desired event code (Test1.xls-Sheet1). Then, in the target workbook (Test2.xls), copy that prepared sheet. The code in the target workbook would look like
Sub Demo1()
Workbooks.Open "Test1.xls"
Sheets("Sheet1").Copy After:=Workbooks("Test2.xls"). _
Sheets(Workbooks("Test2.xls").Worksheets.Count)
Workbooks("Test2.xls").Activate
End Sub
This obviously is a workaround but it works instantly.
A second option could be to prepare the 'workbook-with-one-sheet' and save it as a (sheet) template in D:\Documents and Settings\User\Application Data\Microsoft\Excel\XLSTART. In that case the code can be
Sub Demo2()
Sheets.Add Type:="Test1"
End Sub

Resources