Excel VBA Copy Range Transpose from Another Spreadsheet - vba

I want to copy a range from a workbook and transpose it into my current sheet.
Why would I get a "Subscript out of range" error on this line:
Workbooks("Libraries\Documents\Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
Sub PasteSpecial_Examples()
'https://stackoverflow.com/questions/8852717/excel-vba-range-copy-transpose-paste
'https://www.excelcampus.com/vba/copy-paste-cells-vba-macros/
Workbooks("Libraries\Documents\Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
ActiveSheet.Range("A1").PasteSpecial Transpose:=True
End Sub

Excel only permits one workbook open with a certain filename at the same time, even if those workbooks exist in different directories (which they must, or they couldn't have the same filename).
The Workbooks collection's index is just the filename, not the fully-qualified path and name.
I'm not sure whether the first point is the reason for the second point, or whether the second point is the reason for the first point, but they will be related.
So your code should be:
Sub PasteSpecial_Examples()
Workbooks("Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
ActiveSheet.Range("A1").PasteSpecial Transpose:=True
End Sub
Based on comments implying that you haven't yet opened Libraries\Documents\Book1.xlsx when you run your code, you could do this:
Sub PasteSpecial_Examples()
Dim wsDst As WorkSheet
Set wsDst = ActiveSheet
Workbooks.Open "Libraries\Documents\Book1.xlsx"
Workbooks("Book1.xlsx").Worksheets("Sheet1").Range("A1:A5").Copy
wsDst.Range("A1").PasteSpecial Transpose:=True
End Sub
which continues to refer to the workbook by its name.
Or, slightly better, do this:
Sub PasteSpecial_Examples()
Dim wbSrc As WorkBook
Dim wsDst As WorkSheet
Set wsDst = ActiveSheet
Set wbSrc = Workbooks.Open("Libraries\Documents\Book1.xlsx")
wbSrc.Worksheets("Sheet1").Range("A1:A5").Copy
wsDst.Range("A1").PasteSpecial Transpose:=True
End Sub
which assigns a Workbook object to refer to the newly opened workbook and then uses that object in the Copy statement.
Note: In this code "Libraries\Documents\Book1.xlsx" is a relative reference to the file, e.g. if the current directory was C:\Temp then it would look for the file C:\Temp\Libraries\Documents\Book1.xlsx. You should seriously consider using an absolute reference if possible.

I do it like this:
Dim Finfo As String
Dim FilterIndex As Long
Dim Title As String
Dim ExportFilename As Variant
Dim CopyBook As Workbook
Dim CopySheet As Worksheet
Dim MnthName As String
'Set up file filter
Finfo = "Excel Files (*.xls*),*.xls*"
'Set filter index to Excel Files by default in case more are added
FilterIndex = 1
' set Caption for dialogue box
Title = "Select a the DD Revenue Master file to Export to"
'get the Forecast Filename
ExportFilename = Application.GetOpenFilename(Finfo, FilterIndex, Title)
'Handle file Selection
If ExportFilename = False Then
'No Export File was Selected
MsgBox "No file was selected"
Else
'Check and see if this is a correct Export File
Workbooks.Open (ExportFilename)
Set CopyBook = ActiveWorkbook
Set CopySheet = CopyBook.Worksheets(1)
MsgBox "Valid File Selected."
Application.CutCopyMode = False
revenueSheet.Range("A1:BO500").Copy
CopyBook.Worksheets(1).Activate
CopyBook.Worksheets(1).Range("A1").PasteSpecial Paste:=xlPasteColumnWidths
CopyBook.Worksheets(1).Range("A1").PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False 'erase the clipboard
'close your stuff that you dont want open
End If
End Sub
Don't forget to close your workbooks when you are done. I had to trim a bunch of code because my file launches into a large case select. But often you select a workbook, open it, select some data, copy it, and paste it, close the workbook. Happens alot. Hope this helps. I believe that I found that you had to activate the newly selected workbook to perform actions on it. You can always refer to the workbook with the code in it as ThisWorkbook
To avoid confusion and since they are used in a bunch of modules I have a global variables module with the following in it but you could do this at the top of the sub if you don't have a complex project.
Option Explicit
Public thisWB As Workbook
Public functionSheet As Worksheet
Public revenueSheet As Worksheet
Public salesSheet As Worksheet
Public scratchSheet As Worksheet
Public lastRow As Double
'**********************************************************
'This sub routine will be used to intialize public variables
'**********************************************************
Private Sub SetPublicVariables()
Set thisWB = ActiveWorkbook
Set functionSheet = thisWB.Worksheets("Data Functions")
Set revenueSheet = thisWB.Worksheets("DD Monthly Revenue")
Set salesSheet = thisWB.Worksheets("Salespersons")
Set scratchSheet = thisWB.Worksheets("ScratchSheet")
End Sub
I use this method alot . . . . . .
Oh, I call the public variable set up upon workbook open (you can find that method). In order to call a private sub you must use.
Application.Run "Global_Variables.SetPublicVariables"
'that is modulename.methodname if you want to pass arguments following
'Application.Run "modulename.methodname", arg1, arg2, etc.
Cheers, Happy coding - WWC

Related

How do I copy a range from one workbook to another in excel WITHOUT having to name it in VBA?

I'm looking for assistance regarding how to write a VBA command that allows me to copy a range of cells from different workbooks onto one master sheet. Let me explain further.
Everyday I receive a new excel document from my clients named based on the date it was uploaded ie. September 18, 2018 file would be called A20180918.
Once i've received a week's worth of excel files (A20180918-A20180921), I then have to copy certain information from the original uploaded file to a master tracking sheet.
So, my hurdle is such that each time I record my actions into a macro, the code includes the file name which then creates a subscript error when it's run on the next day's file.
So here's an example below of the code I have this far:
Sub CopyRange()
CopyRange Macro
'This is the line of the code that's causing problems given it's a specified workbook name
'and when I try to open tomorrow's workbook i'll run into the subscript error.
Windows("A20180914.xls").Activate
Range("A2:B2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Windows("Master Sheet.xlsm").Activate
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
Thank you!
Here's two solutions. One to scan an Directory for files, and the other to select files. I see they've both been suggested in the comments already. :p
Sub Test()
' If you want to scan an "unprocessed files" directory
'Call ScanDir("C:\Test\")
' If you want to select files to process
Call SelectFiles
End Sub
Private Sub ScanDir(ByVal DirPath As String)
Dim oCurFile As String
oCurFile = Dir(DirPath)
Do While oCurFile <> ""
' Add the work to the file here
' Filename: DirPath & oCurFile
oCurFile = Dir()
Loop
End Sub
Private Sub SelectFiles()
Dim oFileDialog As FileDialog
Set oFileDialog = Application.FileDialog(msoFileDialogFilePicker)
oFileDialog.AllowMultiSelect = True
If oFileDialog.Show = -1 Then
Dim oFile As Variant
For Each oFile In oFileDialog.SelectedItems
' Add the work to the file here
' Filename: oFile
Next
End If
End Sub
By the looks of it you have all the workbooks open when you run the code - there are ways to have the code open each workbook in a certain folder, or ask the user to select them.
While writing this other answers have given the code for selecting files from folders.
Each workbook in the Excel Application is held in a collection of workbooks. The good thing about collections is you can step through them without know the specifics before you get there.
The code below will print the name of each workbook you have open into the immediate window. Note - these are in the same instance of Excel. If you open Excel a second time then any workbooks in that application will be in a different collection.
You don't really need the Application but I left it in to make things a bit clearer.
Sub Test()
Dim bk As Workbook
For Each bk In Application.Workbooks
Debug.Print bk.Name
Next bk
End Sub
This is the full code - note that nothing is Selected.
Sub Test()
Dim bk As Workbook
Dim Master As Workbook
Dim LastCell As Range
Set Master = Workbooks("Master Sheet.xlsm")
For Each bk In Application.Workbooks
'Checks the middle part of the file name - should be a number.
'Better ways to check the file name are available.
If IsNumeric(Mid(bk.Name, 3, 8)) Then
'Copy date from Sheet1. It's assumed each row in
'column B is populated and figures out the last cell from there.
With bk.Worksheets("Sheet1")
Set LastCell = .Cells(.Rows.Count, 2).End(xlUp)
.Range("A1", LastCell).Copy
End With
'Pastes the results to Sheet1 in the Master workbook.
'The last cell containing data in column A is found and
'then offset by 1 row.
With Master.Worksheets("Sheet1")
.Range("A" & .Rows.Count).End(xlUp).Offset(1).PasteSpecial Paste:=xlPasteValues
End With
End If
Next bk
End Sub
This will do it, you just need to supply the sheet name and ranges where noted:
Sub copyRange()
'File system variables
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim myDir As String
Dim Mask As String
'Workbook variables
Dim wb As Workbook
Dim sh As Worksheet
myDir = "C:\Users\Guest\Desktop" 'Insert the path where your incoming files are stored.
Mask = "*.xl??" 'This makes it so it only looks at Excel files.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.getFolder(myDir)
For Each objFile In objFolder.Files
If LCase(objFile.Name) Like LCase(Mask) Then
Set wb = Workbooks.Open(myDir & "\" & objFile.Name, , True) 'This is set to open in read only, to avoid issues with the file already being open
'The ranges you are copying/pasting are vague, supply the sheet names and ranges below
'Get Copy range with dynamic number of rows.
With wb.Sheets("Sheet1").Range("A2:B2") '<---- Specify Sheet/Range
Set copyRange = .Resize(.End(xlDown).Row, 2)
End With
'Get next available row in paste range.
With ThisWorkbook.Sheets("Sheet1").Range("G:H") '<---- Specify Sheet/Range
Set pasteRange = .Cells(.Rows.Count, 1).End(xlUp).Offset(1, 0)
End With
copyRange.Copy pasteRange
wb.Close False
End If
Next objFile
End Sub

VBA to paste data into existing workbook without specifying workbook name?

I am creating a workbook which will be used as a template for monthly reports (let's call it 'ReportWorkbookTest') and am struggling to write or record a macro which will paste data into the ReportWorkbookTest from various, unspecified workbooks.
To create the monthly reports, data is exported from a server to a .xlsx file named by the date/time the report was exported. Therefore, the name of the workbook which information will be pasted form will always have different names. The columns that the information in the monthly data exports will always remain the same (columns D:G & I). I've managed to do this for two specified workbooks but cannot transpose to new monthly data exports.
Range("I4").Select
Windows("Export 2018-06-21 11.51.34.xlsx").Activate
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=9, Criteria1:= _
xlFilterLastMonth, Operator:=xlFilterDynamic
Range("D2:G830,I2:I830").Select
Range("I2").Activate
Selection.Copy
Windows("ReportWorkbookTest.xlsm").Activate
Selection.PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Is there a way to set up the VBA so that the workbook names do not need to be specified while running the macro? Also, how do I specify that the macro only copies the active rows in the table if the number of rows changes per export?
Thanks!
If only these two workbooks will be open you can use numbers instead of the name:
Workbooks(1)
and
Workbooks(2)
Workbooks(1) will be the one that was opened first, more likely ReportWorkbookTest.xlsm where the macro will be, so you can provide instructions that this file should be opened first. If more than these two workbooks will be open you can try a loop approach, here is an example to use:
Dim wkb as Workbook
Dim thisWb as Workbook
Dim expWb as Workbook
Set thisWb = ThisWorkbook
For Each wkb in Workbooks
If wkb.Name Like "Export 2018-*" Then
expWb = wkb
Exit For
End If
Next
If Not expWb Is Nothing Then
'Found Export, do stuff like copy from expWb to thisWb
expWb.Worksheets(1).Range("B20:B40").Copy
thisWb.Sheets("PasteSheet").Range("A3").PasteSpecial xlValues
Else
'Workbook with Export name not found
End If
This is your framework, if you have multiple files to import then I would suggest a wizard instead.
Wizard framework would be:
1) prompt the user to select a file (of a certain type you might check for, can be a column name - header)
2) if it passes validation then import the data (and process it)
2b) if doesn't pass report it wasn't a valid file and prompt again
3) prompt for the next file type
......
I have a project like this that takes 4 different data "dumps" and merges them into a summary workbook each month.
But for a single file of changing name, here you go for a framework:
you can eliminate cycling through all of the worksheets if there is only one
you might also not be appending data to what already exists, but that is what finding the new last row is for.
Option Explicit
'Sub to get the Current FileName
Private Sub getFN()
Dim Finfo As String
Dim FilterIndex As Long
Dim Title As String
Dim CopyBook As Workbook 'Workbook to copy from
Dim CopySheet As Worksheet 'Worksheet to copy from
Dim FN As Variant 'File Name
Dim wsNum As Double 'worksheet # as you move through the Copy Book
Dim cwsLastRow As Long 'copy worksheet last row
Dim mwsLastRow As Long 'master worksheet last row
Dim masterWS As Worksheet 'thisworkbook, your master worksheet
Dim rngCopy1 As Range
Dim rngCopy2 As Range
Set masterWS = ThisWorkbook.Worksheets("Master Security Logs")
'Set up file filter
Finfo = "Excel Files (*.xls*),*.xls*"
'Set filter index to Excel Files by default in case more are added
FilterIndex = 1
' set Caption for dialogue box
Title = "Select the Current AP Reconcile Workbook"
'get the Forecast Filename
FN = Application.GetOpenFilename(Finfo, FilterIndex, Title)
'Handle file Selection
If FN = False Then
MsgBox "No file was selected.", vbExclamation, "Not so fast"
Else
'Do your Macro tasks here
'Supress Screen Updating but don't so this until you know your code runs well
Application.ScreenUpdating = False
'Open the File
Workbooks.Open (FN)
'Hide the file so it is out of the way
Set CopyBook = ActiveWorkbook
For wsNum = 1 To CopyBook.Sheets.Count 'you stated there will be 8, this is safer
'Do your work here, looks like you are copying certain ranges from each sheet into ThisWorkbook
CopySheet = CopyBook.Worksheets(wsNum) '1,2,3,4,5,6,7,8
'Finds the lastRow in your Copysheet each time through
cwsLastRow = CopySheet.Cells(CopySheet.Rows.Count, "A").End(xlUp).Row
'Set your copy ranges
Set rngCopy1 = CopySheet("D2:D"&cwsLastRow) 'this is your D column
Set rngCopy2 = CopySheet("I2:I"&cwsLastRow) 'this is your I column
'so you would have to keep tabs on what the lastRow of this sheet is too and always start at +1
mwsLastRow = masterWS.Cells(masterWS.Rows.Count, "A").End(xlUp).Row
'Copy the ranges in where you want them on the master sheet
'rngCopy1.Copy destination:= masterWS.Range("D"&mwsLastRow+1)
'rngCopy2.Copy destination:= masterWS.Range("I"&mwsLastRow+1)
'Clear the clipboard before you go around again
Application.CutCopyMode = False
Next wsNum
End If
'Close the workbook opened for the copy
CopyBook.Close savechanges:=False 'Not needed now
'Screen Updating Back on
Application.ScreenUpdating = True
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

Copying range to new workbook

I have come across some code to copy a range to a new workbook, but I'm not sure why it works.
Worksheets("Short Form").Copy
Set wb = ActiveWorkbook
How does this copy the worksheet 'Short Form' to a new workbook when all that the code says is assign the active workbook to the reference 'wb'? It doesn't even employ the .add method. Right now I want to paste values only to this new workbook, but not quite sure how to do so because I don't understand this block of code.
Try this - as the following manual steps are the same as your code snippet:
1.Open a blank workbook
2.Press record macro
3.Right click the Sheet1 workbook tab
4.Select "Move or Copy"
5.In the "To book" combo select (new book)
6.Check the "Create a copy" box so that the window now looks like this:
7.Stop the recorder
8.Go and find your recorded code ...and voila....mine looks like this
Option Explicit
Sub Macro1()
'
' Macro1 Macro
'
'
Sheets("Sheet1").Select
Sheets("Sheet1").Copy
End Sub
Your code is the same as what these manual steps describe.
You must have a line Dim wb as workbook somewhere or it would not run.
This line Set wb = ActiveWorkbook will then make the object wb equal to the new workbook that you have copied into, as it is active, so you can do further operations on it. You can easily switch the workbook that wb is pointed at:
Sub Macro1()
Dim wb As Workbook
ThisWorkbook.Sheets("Sheet1").Copy
Set wb = ActiveWorkbook
MsgBox wb.Name
ThisWorkbook.Activate
Set wb = ActiveWorkbook
MsgBox wb.Name
End Sub
BUT
In my production code I generally never use Set x To ActiveWorkbook I always name the workbook and then use Set x To Workbooks("DefiniteName")
WITHOUT USING CLIPBOARD
If you want to avoid using the clip board then the following example shows how to move values-only data without using paste:
Sub WithoutPastespecial()
Dim firstRange As Range
Set firstRange = ThisWorkbook.Worksheets("Short Form").Range("S4:S2000") 'can change S4:S2000 to the range you want to copy
Dim newBk As Workbook
Dim secondRange As Range
Set newBk = Workbooks.Add
Set secondRange = newBk.Worksheets("Sheet1").Range("A1")
With firstRange
Set secondRange = secondRange.Resize(.Rows.Count, .Columns.Count)
End With
secondRange.Value = firstRange.Value
End Sub
Note this is not copying a Range rather the entire worskheet :)
If you use the method:
Worksheets("Short Form").Cells.Copy
Then you will copy only the cells, not the entire worksheet, and this method will NOT create a new workbook. You can tell it to add a workbook when necessary.
Here is an example:
Option Explicit
Sub CopyNew()
Dim wbNew As Workbook
Dim wb As Workbook
Set wb = ThisWorkbook 'It is a good idea to explicitly control workbooks using either a defined variable like "wb" or the "ThisWorkbook" object, instead of using "ActiveWorkbook" or referring to files by name.
Application.CutCopyMode = False
wb.Sheets("Short Form").Cells.Copy
'Add a new workbook for the values:
Set wbNew = Workbooks.Add
wbNew.Sheets(1).Cells.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub