VBA Excel error when code is moved from worksheet to module - vba

I have a Excel file with multiple tabs. I have a worksheet with some code which is working fine. This code also refers to data on some "master" tabs. I need to duplicate this sheet so I moved the common functions from there to a module. Now I get a VBA 1004 error when trying to access a range on the same worksheet.
Dim selectedRange As Range
Set selectedRange = Worksheets(name).Range("A1", _
Range("A" & Rows.count).End(xlUp)) 'Error Line
This code worked fine till I moved it to a module. It works if I put a
Worksheets(name).Select
before it, but I will have to do it too many times. Based on this query: VBA error 1004 - select method of range class failed
the code should work fine without a .Select. And it does as long as the code is within the worksheet. Why does moving code to a module create a problem?

U use Range and Rows properties without an object qualifier. When used without an object qualifier, this properties are a shortcut for ActiveSheet.Range / ActiveSheet.Rows.
So the code does this:
Worksheets(Name).Range("A1", ActiveSheet.Range("A" & ActiveSheet.Rows.Count).End(xlUp))
But Worksheets(name) could be different from active sheet so better is:
Worksheets(Name).Range("A1", Worksheets(Name).Range("A" & Worksheets(Name).Rows.Count).End(xlUp))
In With-End With block:
With Worksheets(name)
Set selectedRange = .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
End With
So it is ensured that the Range/Rows properties are applied on Worksheets(name) worksheet object.

When you do things on a sheet, you do not really need explicit declarations to that sheet.
However when working on a module and interacting with other sheets, you need to specify which Sheet you want to work with. So select the sheet before you can select the range. To say, SELECT PARENT BEFORE YOU SELECT THE CHILDREN :) Please note, following is just the logic explanantion. Not the exact code syntax.
So I suggest you create the following worksheet variable and set your worksheet object that you need into that.
e.g.
Dim WS as Worksheet
Dim selectedRange As Range
Set WS = Sheets("Shee1")
Set selectedRange = WS.Range("A1", _
Range("A" & Rows.count).End(xlUp)) 'Error Line
Or else if you want to refer to all sheets, you may use each sheet's index
E.g. ThisWorkBook.Sheets(i) 'i is an integer
Then loop or whatever as it deems to your programme structure.
Further you do not have to use Select on the worksheet to point to a range in that worksheet. As per above code you could set the worksheet and set the range you need to process. When optimizing VBA execution, select is usually a taboo. Also Excel 2007 does not retain the active sheet the way older versions used to.

Related

VBA: "Getting run-time 1004: Method 'Range' of object '_Worksheet' failed" when running code on multiple sheets [duplicate]

This script works fine when I'm viewing the "Temp" sheet. But when I'm in another sheet then the copy command fails. It gives an Application-defined or object-defined error:
Sheets("Temp").Range(Cells(1), Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
I can use this script instead, but then I have problems with pasting it:
Sheets("Temp").Columns(1).Copy
Sheets("Overview").Range("C40").PasteSpecial
I don't want to activate the "Temp" sheet to get this.
What else can I do?
Your issue is that the because the Cell references inside the Range 's are unqualified, they refer to a default sheet, which may not be the sheet you intend.
For standard modules, the ThisWorkbook module, custom classes and user form modules, the defeault is the ActiveSheet. For Worksheet code behind modules, it's that worksheet.
For modules other than worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(ActiveSheet.Cells(1), ActiveSheet.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
For worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(Me.Cells(1), Me.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
In either case, the solution is the same: fully qualify the range references with the required workbook:
Dim sh1 As Worksheet
Dim sh2 As Worksheet
Set sh1 = ActiveWorkbook.Sheets("Temp")
Set sh2 = ActiveWorkbook.Sheets("Overview")
With sh1
.Range(.Cells(1,1), .Cells(1,1).End(xlDown)).Copy
End With
sh2.Range("C40").PasteSpecial
Note: When using .End(xlDown) there is a danger that this will result in a range extending further than you expect. It's better to use .End(xlUp) if your sheet layout allows. If not, check the referenced cell and the cell below for Empty first.
I encountered a problem like this myself: I was trying to search through a separate worksheet to see if the color of a cell matched the color of a cell in a list and return a string value: if you are using .Cells(row, column), you only need this:
Sheets("sheetname").Cells(row, column)
to reference that range of cells.
I was looping through a block of 500 cells and it works surprisingly quickly for me.
I have not tried this with .Copy, but I would assume it would work the same way.
This will do, I don't like to use (xlDown) in case a cell is empty.
Dim lRow As Long
lRow = Sheets("Temp").Cells(Cells.Rows.Count, "A").End(xlUp).Row
With Sheets("Temp")
.Range("A1:A" & lRow).Copy Sheets("Overview").Range("C40")
End With
Or if you want to just use Columns...
Sheets("Temp").Columns(1).SpecialCells(xlCellTypeConstants).Copy Destination:=Sheets("Overview").Range("C40")

autofill method of range class failed - destination field contains source field [duplicate]

This script works fine when I'm viewing the "Temp" sheet. But when I'm in another sheet then the copy command fails. It gives an Application-defined or object-defined error:
Sheets("Temp").Range(Cells(1), Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
I can use this script instead, but then I have problems with pasting it:
Sheets("Temp").Columns(1).Copy
Sheets("Overview").Range("C40").PasteSpecial
I don't want to activate the "Temp" sheet to get this.
What else can I do?
Your issue is that the because the Cell references inside the Range 's are unqualified, they refer to a default sheet, which may not be the sheet you intend.
For standard modules, the ThisWorkbook module, custom classes and user form modules, the defeault is the ActiveSheet. For Worksheet code behind modules, it's that worksheet.
For modules other than worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(ActiveSheet.Cells(1), ActiveSheet.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
For worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(Me.Cells(1), Me.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
In either case, the solution is the same: fully qualify the range references with the required workbook:
Dim sh1 As Worksheet
Dim sh2 As Worksheet
Set sh1 = ActiveWorkbook.Sheets("Temp")
Set sh2 = ActiveWorkbook.Sheets("Overview")
With sh1
.Range(.Cells(1,1), .Cells(1,1).End(xlDown)).Copy
End With
sh2.Range("C40").PasteSpecial
Note: When using .End(xlDown) there is a danger that this will result in a range extending further than you expect. It's better to use .End(xlUp) if your sheet layout allows. If not, check the referenced cell and the cell below for Empty first.
I encountered a problem like this myself: I was trying to search through a separate worksheet to see if the color of a cell matched the color of a cell in a list and return a string value: if you are using .Cells(row, column), you only need this:
Sheets("sheetname").Cells(row, column)
to reference that range of cells.
I was looping through a block of 500 cells and it works surprisingly quickly for me.
I have not tried this with .Copy, but I would assume it would work the same way.
This will do, I don't like to use (xlDown) in case a cell is empty.
Dim lRow As Long
lRow = Sheets("Temp").Cells(Cells.Rows.Count, "A").End(xlUp).Row
With Sheets("Temp")
.Range("A1:A" & lRow).Copy Sheets("Overview").Range("C40")
End With
Or if you want to just use Columns...
Sheets("Temp").Columns(1).SpecialCells(xlCellTypeConstants).Copy Destination:=Sheets("Overview").Range("C40")

Interpolation of an entire row [duplicate]

This script works fine when I'm viewing the "Temp" sheet. But when I'm in another sheet then the copy command fails. It gives an Application-defined or object-defined error:
Sheets("Temp").Range(Cells(1), Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
I can use this script instead, but then I have problems with pasting it:
Sheets("Temp").Columns(1).Copy
Sheets("Overview").Range("C40").PasteSpecial
I don't want to activate the "Temp" sheet to get this.
What else can I do?
Your issue is that the because the Cell references inside the Range 's are unqualified, they refer to a default sheet, which may not be the sheet you intend.
For standard modules, the ThisWorkbook module, custom classes and user form modules, the defeault is the ActiveSheet. For Worksheet code behind modules, it's that worksheet.
For modules other than worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(ActiveSheet.Cells(1), ActiveSheet.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
For worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(Me.Cells(1), Me.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
In either case, the solution is the same: fully qualify the range references with the required workbook:
Dim sh1 As Worksheet
Dim sh2 As Worksheet
Set sh1 = ActiveWorkbook.Sheets("Temp")
Set sh2 = ActiveWorkbook.Sheets("Overview")
With sh1
.Range(.Cells(1,1), .Cells(1,1).End(xlDown)).Copy
End With
sh2.Range("C40").PasteSpecial
Note: When using .End(xlDown) there is a danger that this will result in a range extending further than you expect. It's better to use .End(xlUp) if your sheet layout allows. If not, check the referenced cell and the cell below for Empty first.
I encountered a problem like this myself: I was trying to search through a separate worksheet to see if the color of a cell matched the color of a cell in a list and return a string value: if you are using .Cells(row, column), you only need this:
Sheets("sheetname").Cells(row, column)
to reference that range of cells.
I was looping through a block of 500 cells and it works surprisingly quickly for me.
I have not tried this with .Copy, but I would assume it would work the same way.
This will do, I don't like to use (xlDown) in case a cell is empty.
Dim lRow As Long
lRow = Sheets("Temp").Cells(Cells.Rows.Count, "A").End(xlUp).Row
With Sheets("Temp")
.Range("A1:A" & lRow).Copy Sheets("Overview").Range("C40")
End With
Or if you want to just use Columns...
Sheets("Temp").Columns(1).SpecialCells(xlCellTypeConstants).Copy Destination:=Sheets("Overview").Range("C40")

VBA Run-time error 1004 - Declaring a data type based on number of rows in particular range [duplicate]

This script works fine when I'm viewing the "Temp" sheet. But when I'm in another sheet then the copy command fails. It gives an Application-defined or object-defined error:
Sheets("Temp").Range(Cells(1), Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
I can use this script instead, but then I have problems with pasting it:
Sheets("Temp").Columns(1).Copy
Sheets("Overview").Range("C40").PasteSpecial
I don't want to activate the "Temp" sheet to get this.
What else can I do?
Your issue is that the because the Cell references inside the Range 's are unqualified, they refer to a default sheet, which may not be the sheet you intend.
For standard modules, the ThisWorkbook module, custom classes and user form modules, the defeault is the ActiveSheet. For Worksheet code behind modules, it's that worksheet.
For modules other than worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(ActiveSheet.Cells(1), ActiveSheet.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
For worksheet code behind modules, your code is actually saying
Sheets("Temp").Range(Me.Cells(1), Me.Cells(1).End(xlDown)).Copy
Sheets("Overview").Range("C40").PasteSpecial
In either case, the solution is the same: fully qualify the range references with the required workbook:
Dim sh1 As Worksheet
Dim sh2 As Worksheet
Set sh1 = ActiveWorkbook.Sheets("Temp")
Set sh2 = ActiveWorkbook.Sheets("Overview")
With sh1
.Range(.Cells(1,1), .Cells(1,1).End(xlDown)).Copy
End With
sh2.Range("C40").PasteSpecial
Note: When using .End(xlDown) there is a danger that this will result in a range extending further than you expect. It's better to use .End(xlUp) if your sheet layout allows. If not, check the referenced cell and the cell below for Empty first.
I encountered a problem like this myself: I was trying to search through a separate worksheet to see if the color of a cell matched the color of a cell in a list and return a string value: if you are using .Cells(row, column), you only need this:
Sheets("sheetname").Cells(row, column)
to reference that range of cells.
I was looping through a block of 500 cells and it works surprisingly quickly for me.
I have not tried this with .Copy, but I would assume it would work the same way.
This will do, I don't like to use (xlDown) in case a cell is empty.
Dim lRow As Long
lRow = Sheets("Temp").Cells(Cells.Rows.Count, "A").End(xlUp).Row
With Sheets("Temp")
.Range("A1:A" & lRow).Copy Sheets("Overview").Range("C40")
End With
Or if you want to just use Columns...
Sheets("Temp").Columns(1).SpecialCells(xlCellTypeConstants).Copy Destination:=Sheets("Overview").Range("C40")

Transposing Collection of Cells from an Open Workbook

Here's the backstory: I've been enjoying VBA's versatility in Excel 2007, but I'm getting stuck trying to reference a range of cells from another workbook. More explicitly, I'd like to create a columnar list starting in cell I12 in ThisWorkbook using the For Each Next process, which will grab the cell values (of varying quantity) from the first row of the sheet (sheet variable is CompShtName) in the OldFile workbook. Here is the excerpt of code I've written:
CompShtName = Range("E12").Value
For Each Cell In OldFile.Sheets(CompShtName).Range("A1", Range("A1").End(xlRight)) **<---Run-time error 1004**
Range("I" & 12 + i).Value = Cell.Value
i = i + 1
Next
I've declared and defined the variables where: OldFile = file address for the old file, CompShtName = name of sheet in OldFile, and i = counter. When I try to run the code, I get run-time error 1004 in the line containing the arrow.
I thought maybe the problem lay in pulling from another book. But if I change the range to something more simple, like Range("A1:A5"), it works. I even tried removing OldFile.Sheets(CompShtName). and letting it run using the active ThisWorkbook, but that works, too. So the problem must be from try to create an unwieldy collection.
UPDATE
I really like #Zemens approach using transposition instead of a loop. I also made sure to properly qualify my range, using the suggestion from #mehow. Unfortunately, I'm still getting a 1004 error. Here is the updated code exceprt:
Dim OldSht As Worksheet
Dim EndOldHeader As Range
Set OldSht = OldFile.Sheets(CompShtName)
Set EndOldHeader = OldSht.Cells(1, OldSht.Cells(1, Columns.Count).End(xlToRight)) Compare.Range("I12").Resize(EndOldHeader.Columns.Count, 1).Value = Application.Transpose(EndOldHeader.Value)
The error occurs in the Set EndOldHeader line.
The unqualified Range object Range("A1").End(xlRight) is assumed always to refer to the ActiveWorkbook. You need to fully qualify it, per #mehow's comment.
The 1004 error raises because it is not possible to construct a Range object that spans multiple workbooks :)
I find it better when working with large constructs like this to create object variables to represent them. They are easier to read, update, maintain, and debug in the future.
However, you can do this without a loop structure. It should be faster, although might not be noticeably faster.
Dim oldSheet as Worksheet
Dim oldRange as Range
Dim cl as Range
Set oldSheet = OldFile.Sheets(CompShtName)
Set oldRange = oldSheet.Cells(1, oldSheet.Cells(1, Columns.Count).End(xlToRight))
ActiveSheet.Range("I12").Resize(oldRange.Columns.Count,1).Value = Application.Transpose(oldRange.Value)
Or, to do in the loop like:
For each cl in oldRange.Cells
ActiveSheet.Range("I" & 12 + i).Value = cl.Value
i = i + 1
Next