I am new to VBA and basically trying to write my first macro. It is meant to copy entries from one workbook to another omitting repeating values. I have the following code:
Dim s As String
Do While IsEmpty(ActiveCell) = False
If ActiveCell.Value <> s Then
s = ActiveCell.Value
ActiveCell.Offset(1, 0).Select
Windows("Main.xlsm").ActiveCell.Value = s
Windows("Main.xlsm").ActiveCell.Offset(1, 1).Select
Else
ActiveCell.Offset(1, 0).Select
End If
Loop
It gives me Runtime error 1004 for this line:
Windows("Main.xlsm").ActiveCell.Offset(1, 1).Select
Which means that method Select didn't work properly. (I have "Main.xlsm" opened but not active and a cell is selected.)
I searched similar questions and found that it may be because I haven't selected a sheet.
But somehow this line works:
Windows("Main.xlsm").ActiveCell.Value = s
And I can see that cell changed in workbook "Main.xlsm". So, I assume that Windows("Main.xlsm").ActiveCell does create a Range object.
Then why does the error happen? And what is the correct way to write this?
Also I tried changing my code to:
Windows("Main.xlsm").ActiveSheet.ActiveCell.Value = s
Windows("Main.xlsm").ActiveSheet.ActiveCell.Offset(1, 1).Select
Which gave me: "Runtime Error 438 Object doesn't support this property or method". Why is that? What's incorrect here?
And to:
Windows("Main.xlsm").Sheets("Name").ActiveCell.Value = s
Windows("Main.xlsm").Sheets("Name").ActiveCell.Offset(1, 1).Select
Which resulted the same way. And I still don't understand why two last don't work.
This is my first post here so if my editing or anything else is wrong please correct me.
Thanks!
Using ActiveCell, Select etc is not a good idea. Browse through SO Excel tag and you will find many answers advocating against it, many with good explanations why. For one of mine see this question
Instead, declare Range and Worksheet variables and assign these to your ranges and sheets
Dim wbSource as WorkBook
Dim wsSource as WorkSheet
Set wbSource = Application.Workbooks("Main.xlsm")
Set wsSource = wbSource.Worksheets("Name")
To use the workbook the VBA is in, use the ThisWorkbook object
If you want to start with the active sheet, it usuallu OK to use
Dim sh as Worksheet
Set sh = ActiveSheet
To work with a range, use something like
Dim rng as Range
' Set to a specific range
Set rng = Range sh.Range("A1:D10")
' or
With sh
Set rng = .Range(.Cells(1,1), .Cells(10,4))
End With
' Set to all used cells in a column
With sh
Set rng = .Range(.Cells(1,1), .Cells(.Rows.Count,1).End(xlUp))
End With
' Adjust rng
Set rng = rng.Offset(1,0)
Set rng = rng.Resize(10,1)
So, to refactor your code fragment
Dim s As String
Dim rng as Range, cl as Range
Dim shSource as Worksheet
Dim rngDestination as Range
Set shSource = Application.Workbooks("Main.xlsm").Worksheets("SheetName")
Set rngDestination = shSource.Range("A1") ' Change to whatever you need here
Set rng = ActiveCell ' if you must!
' Just in case only one cell entry exists, avoid selecting down to bottom of sheet
If Not IsEmpty(rng.Offset(1,0)) Then
Set rng = Range(rng, rng.End(xlDown)) ' unqualified Range (ie no sh. part) refers to the active sheet
End IF
For Each cl In rng
If cl.Value <> s Then
s = cl.Value
rngDestination.Value = s
Set rngDestination = rngDestination.Offset(1, 1) ' should this be .Offset(1, 0) ?
End If
Next
Related
I was able to copy the rows to another workbook by using predefined ranges. However, I wanted to make sure that it only needs to copy those with values. I've been formulating this code but it returns an error -1004
Private Sub test()
Dim WBa As Workbook, MyPathA As String
Dim FinalRow As Long
Dim getDBsht As Worksheet
MyPathA = "sharepoint.com/Financial Tracker v8.xlsx"
ThisWorkbook.Sheets("ConTracker_DB").UsedRange.ClearContents
' Attempt to open a sharepoint file
Set WBa = Workbooks.Open(MyPathA)
'Set WBb = Workbooks.Open(MyPathB)
Set getDBsht = WBa.Sheets("ConTracker_DB")
getDBsht.UsedRange.Copy
'error starts here
ThisWorkbook.Sheets("ConTracker_DB").UsedRange.Paste
Application.CutCopyMode = False
WBa.Close
Set WBa = Nothing
End Sub
UPDATED CODE: UsedRange fixed my copy rows with value only, but pasting error still persists
You could iterate the individual cells and copy only those which have a value:
Dim sourceCell as Range
Dim targetCell as Range
Set targetCell = ThisWorkbook.Sheets("ConTracker_DB").Range("A1").
For each sourceCell in Range("theNamedRangeYouWantToCopyFrom")
'I'm not sure if you need a dynamic range to copy from, but if you don't...
'...it's much easier to use a "named range" for that.
If sourceCell.Value <> "" Then
targetCell.Value = sourceCell.Value
Set targetCell = targetCell.Offset(1,0)
End If
Next sourceCell
Try defining the final column you need copied and then defining the start and end cells in the range:
FinalCol = 23 '(or whatever you need)
getDBsht.Range(getDBsht.Cells(1, 1), getDBsht.Cells(FinalRow, FinalCol)).Copy
Also note that above I'm specifying which worksheet the cells in the range come from. This can help if you have multiple workbooks/worksheets open at once.
Defining the destination before you start copying is a good idea, too.
CopyTo = ThisWorkbook.Sheets("ConTracker_DB").Range("A1")
Answering my question :))
Basically if you're using usedRange.Copy
you should be pasting with
Cells(1, 1).PasteSpecial Paste:=xlPasteValues
Please find below for the complete code if you're using this:
Private Sub test()
Dim WBa As Workbook, MyPathA As String
Dim FinalRow As Long
Dim getDBsht As Worksheet
MyPathA = "sharepoint.com/Financial Tracker v8.xlsx"
ThisWorkbook.Sheets("ConTracker_DB").UsedRange.ClearContents
' Attempt to open a sharepoint file
Set WBa = Workbooks.Open(MyPathA)
'Set WBb = Workbooks.Open(MyPathB)
Set getDBsht = WBa.Sheets("ConTracker_DB")
getDBsht.UsedRange.Copy
ThisWorkbook.Sheets("ConTracker_DB").Cells(1, 1).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
WBa.Close
Set WBa = Nothing
End Sub
I have one Workbook with multiple projects. Each project has it's own sheet. In each of these sheets there are columns for order numbers ("OrderNub").
In another sheet called "MasterList" contains all of the order numbers across all project. This list is in column A.
I need a function or Macro that will search all of my sheets (bar MasterList) and will display the sheet name in column B.
Below is what I have in Excel:
Option Explicit
Function FindMyOrderNumber(strOrder As String) As String
Dim ws As Worksheet
Dim rng As Range
For Each ws In Worksheets
If ws.CodeName <> "MasterList" Then
Set rng = Nothing
On Error Resume Next
FindMyOrderNumber = ws.Name
On Error GoTo 0
If Not rng Is Nothing Then
FindMyOrderNumber = ws.Range("A1").Value
Exit For
End If
End If
Next
Set rng = Nothing
Set ws = Nothing
End Function
Option Explicit
Function FindMyOrderNumber(strOrder As String) As String
Dim ws As Worksheet
Dim rng As Range
For Each ws In Worksheets
If ws.CodeName <> "MasterList" Then
Set rng = Nothing
On Error Resume Next
Set rng = ws.Columns(1).Find(strOrder)
On Error GoTo 0
If Not rng Is Nothing Then
FindMyOrderNumber = ws.Name
Exit For
End If
End If
Next
Set rng = Nothing
Set ws = Nothing
End Function
Assumptions:
Your project sheets us Table objects. If they don't, you need to edit line 11 to point to whatever range contains the OrderNub data.
If not tables, then your projects at least use the exact same layout. In that case, you could change line 11 to something like: Set rng = ws.Range("C1").EntireColumn.Find(strOrder)
The code name of the master list is MasterList. This is not the same as the worksheet name as seen on the tab. This is the VBA code name. I prefer to use that as it is less likely to be changed and break the check. You can find the codename in the VBA editor. For instance, in this screenshot, the codename for the first worksheet is shtAgingTable and the name - as shown on the tab in Excel - is AgingTable.
This is a function, not a subroutine. That means you don't run it once. It's meant to be used like any other built-in function like SUM, IF, whatever. For instance, you can use the following formula in cell B2:
=FindMyOrderNumber($A2)
I have a workbook named Test and wrote a macros with the code below. It worked fine, but when I added it to my personal workbook, the code gave an error on line Set ws = ThisWorkbook.Sheets("Sheet1").
Subscript out of range.
I moved the code from a module to the Sheet1 on the Personal Workbook and then to the ThisWorkbook. Nothing helped. If you could give any sort of advice of what I could try that would be greatly appreciated.
Sub KeepOnlyAtSymbolRows()
Dim ws As Worksheet
Dim rng As Range
Dim lastRow As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
lastRow = ws.Range("E" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("E1:E" & lastRow)
' filter and delete all but header row
With rng
.AutoFilter Field:=1, Criteria1:="<>*#*"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
' turn off the filters
ws.AutoFilterMode = False
End Sub
Do you specifically wish to refer to the sheet "Sheet1" in the currently open workbook?
If so, use the line below
Set ws = ActiveWorkbook.Worksheets("Sheet1")
And if you simply wish to refer to the current sheet, use
Set ws = ActiveSheet
And if you wish to simply target the first sheet, whatever its name,
Set ws = ActiveWorkbook.Worksheets(1)
The way the code is currently written, it seems to be referring to "Sheet1" in the personal workbook and not necessarily the one currently active with the user.
I am trying to copy the sheet1 range data to sheet2 range but nothing gets copied. This is the full code which I was trying to achieve something but got stuck in the basic place. Please help
Edit: I tried the Macro just now and the same thing happened with this code. Please see the snapshot where you can see that Snap 1 contains source data and also selected but does not get copied to Snap 2. However the ranges are selected there.
Sub copy()
Range("A1:J4").Select
Selection.copy
Sheets("Sheet2").Select
ActiveSheet.Paste
Application.CutCopyMode = False
ActiveWorkbook.Save
End Sub
UpDate
Style-1
Option Explicit
Dim objSheet1,objSheet2,TotalRows,TotalcolCopy,strPathExcel1
Dim oXls : Set oXls = CreateObject("Excel.Application")
Dim aData ': aData = oWb.Worksheets(1).Range("$A2:$C10")
Dim dicP : Set dicP = CreateObject("Scripting.Dictionary")
strPathExcel1 = "D:\WIPData\AravoMacro\Finalscripts\GE_Wing_To_Wing_Report.xlsx"
oXls.Workbooks.open strPathExcel1
Set objSheet1 = oXls.ActiveWorkbook.Worksheets(1)
Set objSheet2 = oXls.ActiveWorkbook.Worksheets(2)
TotalRows=oXls.Application.WorksheetFunction.CountA(objSheet1.Columns(1)) - 3
TotalcolCopy=oXls.Application.WorksheetFunction.Match("Parent Business Process ID", objSheet1.Rows(3), 0)
objSheet1.Range(objSheet1.Cells(4,1),objSheet1.Cells(TotalRows,TotalcolCopy)).Copy(objSheet2.Range("A1"))
'=======================
oXls.ActiveWorkbook.SaveAs strPathExcel1
oXls.Workbooks.close
oXls.Application.Quit
'======================
Style-2
Option Explicit
Dim objSheet1,objSheet2,TotalRows,TotalcolCopy,strPathExcel1
Dim oFS : Set oFS = CreateObject("Scripting.FileSystemObject")
Dim oXls : Set oXls = CreateObject("Excel.Application")
Dim aData ': aData = oWb.Worksheets(1).Range("$A2:$C10")
Dim dicP : Set dicP = CreateObject("Scripting.Dictionary")
oXls.Workbooks.Open(oFs.GetAbsolutePathName("Test.xlsx"))
Set objSheet1 = oXls.ActiveWorkbook.Worksheets(1)
Set objSheet2 = oXls.ActiveWorkbook.Worksheets(2)
TotalRows=oXls.Application.WorksheetFunction.CountA(objSheet1.Columns(1)) - 3
TotalcolCopy=oXls.Application.WorksheetFunction.Match("Parent Business Process ID", objSheet1.Rows(3), 0)
objSheet1.Range(objSheet1.Cells(4,1),objSheet1.Cells(TotalRows,TotalcolCopy)).Copy(objSheet2.Range("A1"))
'=======================
oXls.ActiveWorkbook.SaveAs "Test.xlsx"
oXls.Workbooks.close
oXls.Application.Quit
'======================
Could you people tell me what differences between Style-1 and Style-2.Because in Style-1 all the copied data get saved,which is not the case in Style-2. This design issue mainly the overall problem i was facing from morning.
In what way Style-2 is not perfect?
The simplest way to copy from one range to another would be to recoding macro. Looking at your code, what are the values you are getting for TotalRows and LastCol? Assuming you have matching data returned from your Match(), so there're rows to copy`
Option Explicit
Sub CopyPastes()
Dim rng1 as Range
Dim rng2 as Range
Set rng1 = Sheets(1).Range("B2:C12")
Set rng2 = Sheets(2).Range("B2")
rng1.Copy
rng2.PasteSpecial xlPasteValues
End Sub
So in your case, can you keep it simple? Remove the following line. Just specify the starting Range of the Sheet2 and try out. You do not have to worry about resize at this point.
replace this,
ObSheet2.Range(ObSheet2.Cells(4,1),ObSheet2.Cells(TotalRows,LastCol)).PasteSpecial
with either: coz you are missing paste special argument
ObSheet2.Range(ObSheet2.Cells(4,1),ObSheet2.Cells(TotalRows,LastCol)) _
.PasteSpecial xlPasteValues
or : here without any fancy cells, but direct range
ObSheet2.Range(ObSheet2.Range("A4").PasteSpecial xlPasteValues
I already spent hours on this problem, but I didn't succeed in finding a working solution.
Here is my problem description:
I want to loop through a certain range of cells in one workbook and copy values to another workbook. Depending on the current column in the first workbook, I copy the values into a different sheet in the second workbook.
When I execute my code, I always get the runtime error 439: object does not support this method or property.
My code looks more or less like this:
Sub trial()
Dim Group As Range
Dim Mat As Range
Dim CurCell_1 As Range
Dim CurCell_2 As Range
Application.ScreenUpdating = False
Set CurCell_1 = Range("B3") 'starting point in wb 1
For Each Group in Workbooks("My_WB_1").Worksheets("My_Sheet").Range("B4:P4")
Set CurCell_2 = Range("B4") 'starting point in wb 2
For Each Mat in Workbooks("My_WB_1").Worksheets("My_Sheet").Range("A5:A29")
Set CurCell_1 = Cells(Mat.Row, Group.Column) 'Set current cell in the loop
If Not IsEmpty(CurCell_1)
Workbooks("My_WB_2").Worksheets(CStr(Group.Value)).CurCell_2.Value = Workbooks("My_WB_1").Worksheets("My_Sheet").CurCell_1.Value 'Here it break with runtime error '438 object does not support this method or property
CurCell_2 = CurCell_2.Offset(1,0) 'Move one cell down
End If
Next
Next
Application.ScreenUpdating = True
End Sub
I've done extensive research and I know how to copy values from one workbook to another if you're using explicit names for your objects (sheets & ranges), but I don't know why it does not work like I implemented it using variables.
I also searched on stackoverlow and -obviously- Google, but I didn't find a similar problem which would answer my question.
So my question is:
Could you tell me where the error in my code is or if there is another easier way to accomplish the same using a different way?
This is my first question here, so I hope everything is fine with the format of my code, the question asked and the information provided. Otherwise let me know.
5 Things...
1) You don't need this line
Set CurCell_1 = Range("B3") 'starting point in wb 1
This line is pointless as you are setting it inside the loop
2) You are setting this in a loop every time
Set CurCell_2 = Range("B4")
Why would you be doing that? It will simply overwrite the values every time. Also which sheet is this range in??? (See Point 5)
3)CurCell_2 is a Range and as JohnB pointed it out, it is not a method.
Change
Workbooks("My_WB_2").Worksheets(CStr(Group.Value)).CurCell_2.Value = Workbooks("My_WB_1").Worksheets("My_Sheet").CurCell_1.Value
to
CurCell_2.Value = CurCell_1.Value
4) You cannot assign range by just setting an "=" sign
CurCell_2 = CurCell_2.Offset(1,0)
Change it to
Set CurCell_2 = CurCell_2.Offset(1,0)
5) Always specify full declarations when working with two or more objects so that there is less confusion. Your code can also be written as
Option Explicit
Sub trial()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim Group As Range, Mat As Range
Dim CurCell_1 As Range, CurCell_2 As Range
Application.ScreenUpdating = False
'~~> Change as applicable
Set wb1 = Workbooks("My_WB_1")
Set wb2 = Workbooks("My_WB_2")
Set ws1 = wb1.Sheets("My_Sheet")
Set ws2 = wb2.Sheets("Sheet2") '<~~ Change as required
For Each Group In ws1.Range("B4:P4")
'~~> Why this?
Set CurCell_2 = ws2.Range("B4")
For Each Mat In ws1.Range("A5:A29")
Set CurCell_1 = ws1.Cells(Mat.Row, Group.Column)
If Not IsEmpty(CurCell_1) Then
CurCell_2.Value = CurCell_1.Value
Set CurCell_2 = CurCell_2.Offset(1)
End If
Next
Next
Application.ScreenUpdating = True
End Sub
Workbooks("My_WB_2").Worksheets(CStr(Group.Value)).CurCell_2.Value
This will not work, since CurCell_2 is not a method of Worksheet, but a variable. Replace by
Workbooks("My_WB_2").Worksheets(CStr(Group.Value)).Range("B4").Value