Repeating a VBA / Excel macro across multiple work books - vba

I have 20 excel workbooks - each identical except that they work with different data. I often need to refine and update the formulae, which I then copy from a source workbook to each of the other workbooks in turn.
I use VBA to record a macro (which I know is not that elegant) to copy and paste the changes required. This is fine, but it's tedious to have to copy the macro 20 times, changing the target workbook name each time. My preferred solution is to automate it by setting up a loop and calling the macro for each target in turn.
I am new to macros, and am struggling with the right syntax for this.
I've tried the following, but it does not work. I get a "Object Variable not set" error message, which i don't really understand and can't resolve.
Sub New()
'
Dim i As Integer
Dim target As Workbook
target(1) = "Workbook1.xlsx"
target(2) = "Workbook2.xlsx"
'etc for the other countries
For i = 1 To 20
Update
Next i
End Sub
Sub Update()
' Update macro copies updated cells from workbook Country A.xlsx to target workbook
Windows("Country A.xlsx").Activate
Sheets("Tax").Select
Rows("17:26").Select
Selection.Copy
Windows(target(i)).Activate
Sheets("Tax").Select
Range("A17").Select
ActiveSheet.Paste
' Etc for other changes required
End Sub
Any help on what i'm missing would be much appreciated.

Sub Update is not "seeing" the variable i. Consider:
Sub New()
Dim i As Integer
Dim target As Workbook
target(1) = "Workbook1.xlsx"
target(2) = "Workbook2.xlsx"
'etc for the other countries
For i = 1 To 20
Update (i)
Next i
End Sub
Sub Update(i As Integer)
' Update macro copies updated cells from workbook Country A.xlsx to target workbook
Windows("Country A.xlsx").Activate
Sheets("Tax").Select
Rows("17:26").Select
Selection.Copy
Windows(target(i)).Activate
Sheets("Tax").Select
Range("A17").Select
ActiveSheet.Paste
' Etc for other changes required
End Sub

After trying a few different things, i've found a solution to avoid the problems with the above code. It involves setting up the names of the various target worksheets in an array.
The code below works
Sub Newtest()
'
Dim target As String
Dim targetwb(1 To 2) As String
targetwb(1) = "Workbook3.xlsx"
targetwb(2) = "Workbook4.xlsx"
'
For i = 1 To 2
target = targetwb(i)
Test1 (target)
Next i
End Sub
Sub Test1(target)
'
' Copies updated cells from workbook Country A.xlsx to target workbook
'
Windows("Country A.xlsx").Activate
Sheets("Tax").Select
Rows("17:26").Select
Selection.Copy
Windows(target).Activate
Sheets("Tax").Select
Range("A17").Select
ActiveSheet.Paste
'
End Sub

Related

Copy from one worksheet to another without opening excel using vba

I'm trying to copy cell values from one excel to another using VBA. What I'm trying to do is that by clicking on the script, excel (without getting opened) values should automatically get copied from one worksheet to another. While executing my script I get an error which says Type Mismatch: 'Sheets'. I've read various posts but could not find an answer to this problem. My script looks like this:
ImportData_Click
Sub ImportData_Click()
Dim objExcel2
Set objExcel2 = CreateObject("Excel.Application")
' open the source workbook and select the source sheet
objExcel2.Workbooks.Open "<path>\test1_vbs.xlsx"
' copy the source range
Sheets("Sheet1").Range("A1:B2").Copy
' select current workbook and paste the values
ThisWorkbook.Activate
Sheets("Sheet2").Range("A1:B2").PasteSpecial xlPasteValues
' close the source workbook
Windows("<path>\test1_vbs.xlsx").Activate
ActiveWorkbook.Save
ActiveWorkbook.Close
End Sub
If you are going to tranfer values only between application instances, skip the clipboard and use an array.
Option Explicit
Sub ImportData_Click()
Dim objExcel2 As Object, arr As Variant
Set objExcel2 = CreateObject("Excel.Application")
' open the source workbook and select the source sheet
With objExcel2.Workbooks.Open("c:\test\test2_vbs.xlsx")
' copy the source range to variant array
arr = .Worksheets("Sheet1").Range("A1:B2").Value
' close the source workbook
.Close savechanges:=False
End With
' select current workbook and paste the values
ThisWorkbook.Worksheets("Sheet2").Range("A1").Resize(UBound(arr, 1), UBound(arr, 2)) = arr
' close the source application instance
objExcel2.Quit
Set objExcel2 = Nothing
ActiveWorkbook.Save
ActiveWorkbook.Close
End Sub

VBA to combine all sheets not working. One sheet does not merge

The following is supposed to take every sheet not named "Combined Reports" and merged into the sheet Combined Reports.
My workbook has 5 worksheets in the following order:
Combined Reports
New Leave Capture
Denial Capture
Open Leave Captuer
RTW Capture
My code captures sheets 3 through 5 but it is not capturing sheet 2. Here is my code if anyone can help
Sub combine_all_Reports()
Dim J As Integer
Dim s As Worksheet
On Error Resume Next
For Each s In ActiveWorkbook.Sheets
If s.Name <> "Combined Reports" Then
Application.GoTo Sheets(s.Name).[B9]
Selection.CurrentRegion.Select
Selection.Copy Destination:=Sheets("Combined Reports"). _
Cells(Rows.Count, 1).End(xlUp)(2)
wksCombinedReports.Cells.EntireColumn.AutoFit
End If
Next
End Sub
You should avoid using Application.GoTo, Selection, and Select and instead use fully qualified objects.
Your For loop code, could be much shorter (and faster), see the code below:
For Each s In ActiveWorkbook.Sheets
If s.Name <> "Combined Reports" Then
' copy >> paste in 1 line
s.Range("B9").CurrentRegion.Copy Destination:=wksCombinedReports.Cells(wksCombinedReports.Rows.Count, 1).End(xlUp)(2)
wksCombinedReports.Cells.EntireColumn.AutoFit
End If
Next
since you already know the sheets name in advance you could code like follows:
Option Explicit
Sub combine_all_Reports()
Dim s As Worksheet
With Worksheets("Combined Reports") ' reference "Combined Reports" sheet
For Each s In Worksheets(Array("New Leave Capture", "Denial Capture", "Open Leave Captuer", "RTW Capture")) ' loop through specific sheets
s.Range("B9").CurrentRegion.copy Destination:=.Cells(.Rows.Count, 1).End(xlUp)(2) ' copy current sheet range B9 current region and paste to referenced sheet (i.e. "Combined Reports")
Next
.UsedRange.EntireColumn.AutoFit ' autofit only once all copy&paste have been made
End With
End Sub

Call "ThisWorkbook"

I am trying to switch between a template (hard coded) and a dynamic report which changes name weekly (ThisWorkbook). I am struggling with calling the variable x to bring focus to the workbook. I am copying the template formulas and pasting them into the dynamic report.
Sub wkbk()
Dim x As Excel.Workbook
Set x = ThisWorkbook
Dim pth As String
pth = x.FullName
Windows(pth).Activate
End Sub
Here is the VBA code I am using:
Windows("BBU_CMD_TEMPLATE.xlsx").Activate
Cells.Select
Selection.Copy
Windows(pth).Activate
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Paste
Why not just use ThisWorkbook.Activate? There's generally no need to assign a variable to represent a built-in like ThisWorkbook so the rest of those variables are unnecessary unless you're using them elsewhere in that procedure (from the snippet provided, you aren't, so you don't need them).
Sub wkbk()
ThisWorkbook.Activate
End Sub
However, what's the point of wkbk procedure? If solely to activate the workbook, that's not needed either and there are plenty of reasons to avoid Activate.
Sub CopySheetFromTemplateToThisWorkbook()
Dim tmplt As Workbook
On Error Resume Next
Set tmplt = Workbooks("BBU_CMD_TEMPLATE.xlsx")
If tmplt Is Nothing Then
MsgBox "Template file needs to be open..."
Exit Sub
End If
On Error GoTo 0
With ThisWorkbook
tmplt.ActiveSheet.Copy After:=.Sheets(.Sheets.Count)
End With
End Sub

Excel Macro: Setting a variable for a workbooks location?

I need to write a macro script that will copy data from one xml workbook and paste the values to another workbook. I've written the below macro that works fine, but i need to run this every week for several different documents so it means i have to replace the document name for each run.
Here's what i have so far:
Sub copying()
''''''Section 1''''''
Workbooks("Results_2561").Activate 'workbook i'm copying from
Range("B27:B41").Select
Selection.Copy
Workbooks("Overall_Results").Activate 'workbook i'm pasting to
Range("G2").PasteSpecial
''''''Section 2''''''
Workbooks("Results_2561").Activate
Range("C27:C41").Select
Selection.Copy
Workbooks("Overall_Results").Activate
Range("C2").PasteSpecial
''''''Section 3''''''
Workbooks("Results_2561").Activate
Range("I28:I40").Select
Selection.Copy
Workbooks("Overall_Results").Activate
Range("G17").PasteSpecial
''''''Section 4''''''
Workbooks("Results_2561").Activate
Range("J28:J40").Select
Selection.Copy
Workbooks("Overall_Results").Activate
Range("C17").PasteSpecial
End Sub
...
and that's only half the script. Is there a way i can declare a variable at the start and set it as the Workbooks file path so i can call that instead of typing and retyping it over and over again?
Preferably without using something like
Dim book1 as Workbook
Set book1 = Workbooks.Open("C://Results_2561.xlsm")
..as this keeps opening and closing the document when i run the script.
Thanks
since you're only interested in copying values you could use this helper Sub
Sub CopyValues(rngToCopyFrom As Range, rngToCopyTo As Range)
With rngToCopyFrom
rngToCopyTo.Resize(.Rows.COUNT, .Columns.COUNT).Value = .Value
End With
End Sub
to be exploited in your main code like follows:
Sub main()
Dim wsTo As Worksheet
Set wsTo = Workbooks("Overall_Results").ActiveSheet '<--| set the worksheet to paste values to
With Workbooks("Results_2561").ActiveSheet '<--| reference the worksheet to copy values from
CopyValues .Range("B27:B41"), wsTo.Range("G2")
CopyValues .Range("C27:C41"), wsTo.Range("C2")
CopyValues .Range("I28:I40"), wsTo.Range("G17")
CopyValues .Range("J28:J40"), wsTo.Range("C17")
End With
End Sub
should your relevant workbooks have more than one sheet, then just substitute
ActiveSheet
with
Worksheets("myRelevantShetName") '<--|change "myRelevantShetName" to the actual name of the relevant worksheet in each workbook
First of all, you don't have to Activate workbook every time when you want to copy/paste something. Just declare it in Range() property, for example:
''''''Section 1''''''
Workbooks("Results_2561").Sheets(1).Range("B27:B41").Copy
Workbooks("Overall_Results").Sheets(1).Range("G2").PasteSpecial
You can set Workbook as variable like:
Sub copying()
Dim wb1 As Workbook, wb2 As Workbook
Set wb1 = Workbooks("Results_2561")
Set wb2 = Workbooks("Overall_Results")
''''''Section 1''''''
wb1.Sheets(1).Range("B27:B41").Copy
wb2.Sheets(1).Range("G2").PasteSpecial
End Sub
Finally, as #A.S.H suggested, you can add a file dialog where you point which files you want to use. I have put it in some function (don't forget to put it in the same project as your copying macro):
Function strPath() As String
Dim intResult As Integer
Application.FileDialog(msoFileDialogFilePicker).Title = "Select file"
intResult = Application.FileDialog(msoFileDialogFilePicker).Show
If intResult <> 0 Then
strPath = Application.FileDialog(msoFileDialogFilePicker).SelectedItems(1)
End If
End Function
So your final code for Section 1 would look like:
Sub copying()
Dim wb1 As Workbook, wb2 As Workbook
MsgBox "Show file to copy form."
Set wb1 = Workbooks.Open(strPath())
MsgBox "Show file to paste in."
Set wb2 = Workbooks.Open(strPath())
''''''Section 1''''''
wb1.Sheets(1).Range("B27:B41").Copy
wb2.Sheets(1).Range("G2").PasteSpecial
End Sub

Splitting up Copy/Paste without using Select

I am writing an Excel macro in VBA. In a certain part of my code, I need to copy and paste one unmerged cell to a merged cell in a separate workbook. Part of this process is repetitive. The most efficient way for me to achieve this is to copy the cell and then Call a separate Sub with the location it will be pasted into. I have written a version using Select that works:
Sub Macro1()
Workbooks("Book1.xlsm").Worksheets("Sheet1").Activate
ActiveSheet.Range("A1").Copy
Call Macro2
End Sub
Sub Macro2()
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Activate
ActiveSheet.Range("E46:G46").Select
Selection.PasteSpecial Paste:=xlPasteFormulasAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
End Sub
But I have been trying to move away from Select and this is the code I came up with:
Sub Macro1()
Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Copy
Call Macro2
End Sub
Sub Macro2()
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").PasteSpecial xlPasteFormulasAndNumberFormats
End Sub
This code gives me a 'PasteSpecial method of Range class failed' on the only line of Macro 2.
Is there a way to copy the contents of a cell and then Call a separate Sub with the destination and paste method, without using Select?
Edit: I understand that setting the values equal to each other is simpler than copy/paste. However, in this instance, that is not a possibility.
I also understand the pitfalls of merged cells. I get it. Seriously. But I don't have a choice as I am working with a customer's workbook, which can't be changed.
Edit #2: Following a comment from #ExcelHero, I rebooted my computer and ran the macro again. It worked. I have no idea what was going wrong. Regardless, credit to #ExcelHero for recognizing that my code was correct.
Why a second macro?
Anyways, if it's absolutely necessary, you're going to need to edit your second macros slightly.
Sub Macro1()
Dim copyString As String
copyString = Worksheets("Sheet1").Range("A1").Value
Call Macro2(copyString)
End Sub
You'll notice that now Macro2 is called with copyString after it. This means, that you will run Macro2, and do something with whatever value is set to copyString.
Sub Macro2(text As String)
'Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = text
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = text
End Sub
In this macro, you'll see I added (text as String) to the macro name. This will take some value (from Macro1, the value is the copyString variable), and then use it in the macro. Does that make sense?
Again, I would only do this way if you absolutely need two macros for this copy/pasting. If you can use just one macro, then these can be combined into a few simple lines:
Sub Macro3()
Dim copyString As String
copyString = Worksheets("Sheet1").Range("A1").Value
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = copyString
End Sub
...or even further, cut out the copyString middleman (as #findwindow mentioned):
Sub macro4()
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = Worksheets("Sheet1").Range("A1").Value
End Sub
This format worked for me - simply defining your copy range as a range and passing it to the first sub, and then iterating through the loop of declaring subsequent cells as the paste range, and passing that range on to the pasting sub:
Sub copy_paste()
Dim rng As Range
Set rng = Sheets(1).Range("A1")
Call copy_start(rng)
End Sub
Sub copy_start(ByVal copyRange As Range)
Dim rng As Range
copyRange.copy
For i = 1 To 5
Set rng = Sheets(1).Range("B" & i)
Call pasteover(rng)
Next i
Application.CutCopyMode = False
End Sub
Sub pasteover(ByVal pasteRange As Range)
pasteRange.PasteSpecial Paste:=xlPasteFormulasAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
End Sub
The error resulted from the copy/pasting that I was doing while I was editing my code. Because the code I was copying was still on the clipboard, the macro tried to paste that into the cell, causing the error.
Lessons Learned:
Limit use of the clipboard in VBA
When copy/pasting in the coding window, double check to make sure that you are not affecting the existing code