For each in VBA - starting from third stylesheet - excel

I've hot many stylesheets in Workbook in Excel and I need to make some changes in almost every stylesheet. First two of them and last few should be skipped.
I'm using For Each loop and really don't have idea how to skip 2 first elements. With the last ones is not problem, because I check the stylesheet's name and if it's equal to my condition I'm breaking the loop and exit.
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
ws.Activate
If ws.Name = "03.2016PTF" Then Exit For
'here's my code
Next
If I will exit from loop at start I won't do changes in the rest of documents.

Try:
With ThisWorkbook
maxsht = .Sheets.Count
For i = 3 To maxsht
If .Sheets(i).Name = "03.2016PTF" Then Exit For
'here's my code
Next i
End With
It loops through all sheets using the Index, so effectively you can start at the nth sheet, in this case the third.
The code loops until the last sheet in the wb unless it encounters the aforementioned sheet named 03.2016PTF, at which the loop exits.

Using And on the if statement to check for more criterias.
Or any of them could trigger the if.
And them all must be true so the if is triggered.
This way you can fill in as much sheets as you need and won't worry about their order.
Dim ws As Worksheet, arrWs As Variant, Skiped As Boolean
arrWs = Array("Sheet1ToAvoid", "Sheet2ToAvoid", "...") 'fill this with as much sheets you need
For Each ws In ThisWorkbook.Worksheets
Skiped = Application.Match(ws.Name, arrWs, 0)
If Not Skiped Then
'here's my code
End If
Next

Related

Skip some worksheets in loop function in Excel VBA

I want to have a button replace the info on MOST sheets with the info from a master sheet when a button is clicked. However, I want it to skip some sheets.
I have the below code that works, but there are 2 sheets I want it to skip when running. How can I specify that is skip the sheets named "Dates" and "Monthly"
Sub Button4_Click()
Dim wsVar As Worksheet
For Each wsVar In ThisWorkbook.Sheets
With wsVar
.Range("B9:M30").Value = Worksheets("BASE").Range("B9:M30").Value
End With
Next wsVar
End Sub
You can use an IF statement to check the name of the worksheet and then act accordingly.
For Each wsVar In ThisWorkbook.Sheets
If wsVar.Name = "foo" Or wsVar.Name = "bar" Then
' do nothing
Else
With wsVar
.Range("B9:M30").Value = Worksheets("BASE").Range("B9:M30").Value
End With
End If
Next wsVar
This code will exclude all sheets listed in the Exclude array. Note that sheets name comparisons are carried out case-insensitive and leading or trailing blanks are deemed unintentional and removed.
Sub Button4_Click()
Dim wsVar As Worksheet
Dim Exclude() As String
Dim i As Integer
Exclude = Split("Dates,Monthly", ",")
For Each wsVar In ThisWorkbook.Worksheets
With wsVar
For i = UBound(Exclude) To 0 Step -1
If StrComp(wsVar.Name, Trim(Exclude(i))) = 0 Then Exit For
Next i
If i >= 0 Then .Range("B9:M30").Value = Worksheets("BASE").Range("B9:M30").Value
End With
Next wsVar
End Sub
With a small change, you might convert this same function to process only listed sheets. To identify sheets for action positively, rather than negatively, is the safe way, generally speaking. But if you decide to do that, please also do rename the array :-)

Excel VBA index out of bounds while using Sheets.delete

After a few iterations of the following code I always get the error that the index was out of bounds.
For i = myworksheet.index To Worksheets.count
Sheets(i).delete
Next i
You shouldn't delete sheets this way.
Consider what you're asking it to do. Let's say you have 5 worksheets- Sheet1, Sheet2.. Sheet 5.
Let's say myworksheet is Sheet3 (i=3).
When the loop starts, i is 3.
Sheet3 is deleted.
The loop restarts and i is now 4.
However, there are now only 4 worksheets. So Sheet5 (i=4) is deleted.
The loop restarts and i is now 5.
However, there are now only 3 worksheets. There is no worksheet with index of 5 to delete.
One (of many) ways to achieve your goal is to do the following:
i = myworksheet.Index
Do Until Worksheets.Count = i - 1
Worksheets(Worksheets.Count).Delete
Loop
One thing to point out with this.. in your code, you appear to be deleting your start sheet myworksheet. Because of this, the Do Until... loop I've created finishes at i-1 to stop after myworksheet is deleted. If you didn't want this to happen, remove the - 1. If you did want this to happen, you need to be aware that it will error if the index of myworksheet is 1 - as all workbooks must contain at least 1 worksheet.
Try the next way, please. In your code, after sheets deleting, the reference not make sense for i bigger then existing maximum (remained) one:
Sub deleteSheets()
Dim sh As Worksheet, ws As Worksheet
Set sh = ActiveSheet
For Each ws In ActiveWorkbook.Sheets
If ws.Index > sh.Index Then
Application.DisplayAlerts = False
ws.Delete
Application.DisplayAlerts = True
End If
Next
End Sub
Or looping backwards, as #SJR suggested:
Sub deleteSheetsBis()
Dim myworksheet As Worksheet, i As Long
Set myworksheet = ActiveSheet
For i = ActiveWorkbook.Worksheets.count To myworksheet.Index Step -1
Application.DisplayAlerts = False
Sheets(i).Delete
Application.DisplayAlerts = True
Next i
End Sub

object required error 424 when copying a sheet from closed workbook

I got the rest of my other sub working the way I want it to. It just highlights cells of interest and hides rows that are irrelevant to me across a number of worksheets. One of the last manual processes for me in this is copying a worksheet into the report every day to run the sub. I finally got it working as a standalone sub:
Sub OrbitAdd()
Dim wbOrbit As Workbook
Dim wbTop As Workbook
Set wbTop = ActiveWorkbook
Set wbOrbit = Workbooks.Open("C:\Users\*****\Orbit.xlsx")
wbOrbit.Sheets(1).Copy after:=wbTop.Sheets(7)
wbOrbit.Close SaveChanges:=False
End Sub
When I went to incorporate this into the main sub is when it broke. I tried a basic Call OrbitAdd() and it threw an error (I forget which error). I think it was looking for some arguments to pass to the sub maybe? When I tried to copy this in there directly it would run and add the worksheet and close the workbook before throwing the object required 424 error. As far as I can tell it didn't do anything after closing the workbook.
Why is this working as an independent sub, and not no matter how I try to incorporate it into the main sub? What am I missing or need to do to transition from this block of code back to the main sub to handle the error? (the **** in the file path are to obscure irrelevant file path info)
edit: adding the next few lines from my main sub for further troubleshooting help. The only thing before the first shared code block is turning off screen updating and dimensioning parameters for the rest of the sub.
'sets Orbit range to the size of the eNodeB site list
With Sheet8
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Set Orbit = .Range("A1").Resize(lastRow, 1)
End With
'Clearing conditional formatting from the workbook.
For Each ws In ThisWorkbook.Worksheets
ws.Cells.FormatConditions.Delete
Next ws
'iterates each worksheet with the tables to apply the formatting.
For j = 1 To 3
If j = 1 Then
Set ws = Sheet2
ElseIf j = 2 Then
Set ws = Sheet3

VBA code to call different macro depending on part of Worksheet name

I am working on a macro that will cycle through all of the sheets in the active workbook and will then clear a certain part of a particular worksheet, based on whether one of the relevant keywords is contained in the worksheet name. In each case the worksheet name will be different, but any I want to clear will contain one of the key words below.
I have set up a separate macro to clear the range of cells in each case. If the Worksheet name does not contain any of the keywords, I want the macro to move onto the next worksheet.
My ultimate aim is to be able to apply this to numerous different workbooks, as the project I am working on is split by region, with a separate Excel file per region.
The code I have been trying is below. There are no errors appearing when I run the code, the code does not seem to run either, in fact nothing at all happens!
Any guidance or advice would be greatly appreciated.
Sub Loop_Customer_Sheets()
Dim ws As Integer
Dim i As Integer
ws = ActiveWorkbook.Worksheets.Count
For i = 1 To ws
If ActiveSheet.Name Like "*ABC*" Then
Call ABCInfoClear
ElseIf ActiveSheet.Name Like "*DEF*" Then
Call DEFInfoClear
ElseIf ActiveSheet.Name Like "*GHI*" Then
Call GHIInfoClear
Else:
End If
Next i
End Sub
"Nothing at all happens" - fixing the issue with your code:
Your issue is that you are looping through the number of sheets, but you are only checking the ActiveSheet, which never changes! Replace your code with
ws = ActiveWorkbook.Worksheets.Count
For i = 1 To ws
With ActiveWorkbook.WorkSheets(i)
If .Name Like "*ABC*" Then
ABCInfoClear
ElseIf .Name Like "*DEF*" Then
DEFInfoClear
ElseIf ActiveSheet.Name Like "*GHI*" Then
GHIInfoClear
End If
End With
Next i
Note: you don't need the Call keyword, you can just call subs as presented above.
Alternative solutions
A better option than having numerous macros might be to create a generic sub like
Sub ClearRangeInSheet(rangeAddress As String, sh As WorkSheet)
Dim myRange As Range
Set myRange = sh.Range(rangeAddress)
myRange.ClearContents
' Any other cell clearing code e.g. for formatting here
End Sub
Then call in the loop
Dim wsCount as Long
wsCount = ActiveWorkbook.WorkSheets.Count
For i = 1 to wsCount
With ActiveWorkbook
If .WorkSheets(i).Name Like "*ABC*" Then
' Always pass ".WorkSheets(i)", but change the range address as needed
ClearRangeInSheet("A1:A20", .WorkSheets(i))
ElseIf ' Other worksheet name conditions ...
End If
End With
Next I
As suggested in the comments, you could ditch indexing the sheets, and just loop through the sheet objects themselves:
Dim wksht as WorkSheet
For Each wksht In ActiveWorkbook.WorkSheets
If wksht.Name Like "*ABC*" Then
' Always pass wksht but change the range address as needed
ClearRangeInSheet("A1:A20", wksht)
ElseIf ' Other worksheet name conditions ...
End If
Next wksht

Avoiding .Activate with ActiveX Form Control

I'm updating a macro that's used in lots of spreadsheets, and it's rather slow. While looking to speed it up, I noticed that at one point it goes through this loop:
For each wsLoop in ThisWorkbook.Worksheets
wsLoop.Activate
With ActiveSheet.Status_Text
If bStatus = True Then
.ForeColor = &HC000&
.Caption = "ONLINE"
Else
.ForeColor = &HFF&
.Caption = "OFFLINE"
End If
End With
Next wsLoop
Where wsLoop is a worksheet, bStatus is a boolean and Status_Text is the name of an ActiveX label form control on each worksheet.
I know using .Activate is bad practice and can slow things down, so I dropped the wsLoop.Activate and changed the next line to With wsLoop.Status_Text, but now I get a "Method or data member not found" error message.
What's the proper way to do this?
Interesting question which seems to touch on some poorly-documented features of Excel VBA. It seems that maybe the expression ActiveSheet.Status_Text is operating as the name of the control, with the dot acting as a namespace qualifier, but that in wsLoop.Status_Text VBA is interpreting the dot as the method/property access operator and is correctly giving the error message that no such method or property exists. To reproduce the problem, I created a label named Status_Text on each sheet and then ran
Sub test1()
Dim ws As Worksheet
For Each ws In Worksheets
Debug.Print ws.Status_Text.Caption 'fails
Next ws
End Sub
It crashes with the error that you show. One workaround (although just why it works is mysterious) is to change the loop index from being a Worksheet to a Variant:
Sub test2()
Dim ws As Variant
For Each ws In Worksheets
Debug.Print ws.Status_Text.Caption 'succeeds
Next ws
End Sub
The odd thing about this last example is if you add the line Debug.Print TypeName(ws) in the for-each loop it prints Worksheet so ws.Status_Text works if ws is a variant which holds a worksheet but not if ws is actually a worksheet. The mystery is in some sense deepened but in another sense lessened when you step through this sub in the debugger, looking at the locals window. The type of ws in the loop is described as Variant/Object/Sheet1 (in the first pass through the loop). The specific sheet seems to be part of the current subtype of the variable.
Another workaround is to use a For-Next loop rather than a For-Each:
Sub test3()
Dim i As Long
For i = 1 To Worksheets.Count
Debug.Print Worksheets(i).Status_Text.Caption 'succeeds
Next i
End Sub
You could use either of these approaches to get a reference to the label without having to activate the sheet.

Resources