Selecting a Range while looping through worksheets in VBA - excel

I've shortened my code a bit for the purposes of the question, but the error I'm getting is the same.
When trying to select the cells with data in column A on each worksheet and doing stuff with it, I get an error after the first worksheet:
Sub quickSub()
Dim sh As Worksheet
For Each sh In Worksheets
sh.Range("A6", Range("A6").End(xlDown)).Select
''Random bits of code here where I manipulate selection on each worksheet
Next
End Sub
The error I get is:
"Run-time error '1004': Method 'Range' of object'_Worksheet' failed.

Try this:
sh.Activate
sh.Range("A6", "A" & sh.Range("A6").End(xlDown).row).Select
I made sure the range reference was end downing on the right sheet
And I had the end down return the final row number and concatenate with the column letter which may not be needed, but may make it easier for you to debug.
Update:
Added activate line. Selection may require the sheet be active.
Update2:
Here's the 'Right' way to do this WITHOUT using select
Using this method directly referneces the worksheet data INSTEAD of needing to move around worksheet by worksheet.
This best practice will increase your code performance
Sub quickSub()
Dim sh As Worksheet
For Each sh In Worksheets
With sh.Range("A6", "A" & sh.Range("A6").End(xlDown).row)
'- lines that manipulate the 'selection' in the above with
.Value = "NewValue"
.font.bold = true
End With
''Random bits of code here where I manipulate selection on each worksheet
Next
End Sub

Related

How do you select a specific cell on a specific sheet excel VBA? [duplicate]

During the process of running a script if I manually remove focus from the Workbook containing the macro I get the error quoted. If I don't click on anything it works without issue. Script errors out only when I'm trying to place selection back into A1 from the "Input" sheet. Break point is on following line:
ThisWorkbook.Sheets("Input").Range("A1").Select
If I debug and place focus back on macro Worksheet the script completes without issue. Previous line:
ThisWorkbook.Sheets("Input").Cells.Delete
runs without error so I'm guessing its the range that is falling out of scope but don't quite understand why as it should be defined by the previous scope notations.
Can someone explain why that line is falling out of scope? Shouldn't the ThisWorkbook define fairly explicitly the Workbook that my code is referencing? Any guidance is greatly appreciated.
It doesn't have anything to do with the reference to ThisWorkbook at all. You simply can't Select a Range in an object that isn't active. Consider this code, which exhibits the same error:
Private Sub OneOhOhFour()
'Executing with Book1.xlsm active and Book2.xlsx open.
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
Debug.Print ThisWorkbook.Name
'Outputs 'Book1.xlsm' to Immediate window.
wb.Sheets("Sheet1").Range("A1").Select 'Error 1004
End Sub
Same thing with Worksheets:
Private Sub OneOhOhFourVTwo()
'Starting on anywhere but Sheet2 gives an error.
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet2")
ws.Range("A1").Select 'Error 1004.
End Sub
The simple solution is to Activate the object before you Select within it:
Private Sub NoOneOhOhFour()
Dim wb As Workbook
Set wb = Application.Workbooks("Book2.xlsx")
wb.Activate
wb.Sheets("Sheet1").Range("A1").Select 'No error.
End Sub
Even better is using references and trying to avoid using the Selection and Active* objects entirely.

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

Find a match in workbook using Excel VBA: run-time error 1004

I am trying to create a macro which searches for the word "Routing" in all sheets of my active workbook.
When it detects the first occurrence in one of my sheets of my workbook, I want my macro to select the related cell on the corresponding sheet.
My macro returns the error message:
run-time error 1004: activate method of range class failed…
It seems this line of code generates the issue: Loc.Activate
Sub FindAndExecuteRouting()
Dim Sh As Worksheet
Dim Loc As Range
For Each Sh In ActiveWorkbook.Worksheets
With Sh.UsedRange
Set Loc = .Cells.Find(What:="Routing")
If Not Loc Is Nothing Then
Loc.Activate
End If
End With
Next
End Sub
You can't Activate a Range unless it's on the ActiveSheet.
So you'd have to do Sh.Activate before you can do Loc.Activate. Note that normally you do not want to Activate a cell you already have a reference to, but when the goal is to select a cell so that the user can see the selection box around a specific cell... well, that's one of the very few legit use cases for Worksheet.Activate =)
Public Sub FindAndExecuteRouting()
Dim Sh As Worksheet
Dim Loc As Range
For Each Sh In ActiveWorkbook.Worksheets
With Sh.UsedRange
Set Loc = .Find(What:="Routing")
If Not Loc Is Nothing Then
Sh.Activate
Loc.Activate
Exit Sub
End If
End With
Next
End Sub
Note the Exit Sub: you want to stop looping (Exit For would work as well) once you've found one, otherwise you'll just successively select every result very quickly, and only the last one will end up selected.
Consider using an indenter to help keep indentation consistent!

How to break links to values in VBA on a protected worksheet? Run-time error '1004'

I'm trying to break the links to values while copying over multiple worksheets from one workbook to another in VBA.
I am presented with Run-time error '1004': the sheets are protected, however I am unable to unprotect these sheets.
The problem hits at the .Value = .Value part of the function where it runs the error. Is there another method I can use?
I have tried to wb.BreakLink link function however this was unsuccessful.
ActiveWorkbook.Worksheets(1).Copy After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
With ActiveSheet.UsedRange
For Each smallrng In Range("G16:Q16,G40:Q69").Areas
With smallrng
.Value = .Value
I expect the output to remove all formulas and present only values within the specified range once copied over.
Copying the worksheet is also copying the protection mode. So, if you can create the sheet without copying it, then you should be able to transfer values to the new worksheet.
Option Explicit
Sub foo()
Dim newWS As Worksheet
Dim protectedWS As Worksheet
Dim smallRng As Range
Set protectedWS = Worksheets(1)
Set newWS = Worksheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
protectedWS.Cells.Copy
With newWS
.Range("A1").PasteSpecial xlPasteAll
For Each smallRng In protectedWS.Range("G16:Q16,G40:Q69").Areas
.Range(smallRng.Address).Value = smallRng.Value
Next
End With
End Sub

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