so currently i have this code that loops through a folder of mine specified by me and goes through every single file in the folder to extract Ranges "A18,A19,A14" and copy it to the current worksheet.
However now i need to change the way it works, now i would like to have a main sheet that contains the button to generate the coding that i have written as shown below but in a new sheet.
So basically what i need now is to have a main control sheet that only contains buttons , then do changes to the coding so that it creates a new sheet and generate the data there in the new sheet instead of the main control sheet.
Here is the image for the control sheet
Starting
And here is the end result i wish to get
What i need to see
Also, i have tried myself before asking this question on adding new sheets however it doesn't work
,here is the image of it
Not Working
Somehow it just extracts from one file when its supposed to extract 6 lines as shown in the "What i need to see" Picture.
I really appreciate any help, but if possible pls provide me the small part of the code needed to make this work!
Here is what i have currently
Sub ScanFiles()
Dim myFile As String, path As String
Dim erow As Long, col As Long
path = "c:\Users\Desktop\Tryout\"
myFile = Dir(path & "")
Application.ScreenUpdating = False
Range("A2:I20").ClearContents
Range("A1") = "Test"
Range("B1") = "Temp"
Range("C1") = "Start"
Range("D1") = "Type"
Do While myFile <> ""
Workbooks.Open (path & myFile)
Windows(myFile).Activate
ActiveSheet.Name = "Sheet1"
Set copyrange = Sheets("Sheet1").Range("A18,A19,A14,A19")
Windows("Reset.xlsm").Activate
erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
col = 1
For Each cel In copyrange
cel.Copy
Cells(erow, col).PasteSpecial xlPasteValues
col = col + 1
Next
Windows(myFile).Close savechanges:=False
myFile = Dir()
Loop
Debug.Print myFile
Range("A:I").EntireColumn.AutoFit
Application.ScreenUpdating = True
End Sub
Ok, here try this.. Something I quickly wrote (busy at work). I have made a few assumptions being the Folder you loop thru has only the excel files you need to loop over. 2nd assumtion is each file only has 1 tab. These 2 assumptions are easily fixed if wrong.
I have made reference to Microsoft Scripting Runtime for FSO. DIR is a dead, I only ever use FSO as it much more useful and you can nest FSO loops (something you can't do with DIR)
Also included is an array to store your CopyRange to we can make you code nater and use a single For Loop.
Option Explicit
Sub ScanFiles()
Application.ScreenUpdating = False
Dim wkb As Workbook
Set wkb = ThisWorkbook
Dim wks As Worksheet
Set wks = Worksheets.Add
wks.Name = "NewWorksheet"
' Add Worksheet to accept data
With wks
'.Range("A2:I20").ClearContents -> No longer needed as you create a new sheet
.Range("A1:D1") = Array("Test", "Temp", "Start", "Type")
End With
' Set your copy ranges
Dim CopyRange(1 To 4) As String
CopyRange(1) = "A18"
CopyRange(2) = "A19"
CopyRange(3) = "A14"
CopyRange(4) = "A19"
' Early Binding - Add "Microsoft Scripting Runtime" Reference
Dim FSO As New Scripting.FileSystemObject
' Set FolderPath
Dim FolderPath As String
FolderPath = "c:\Users\Desktop\Tryout\"
' Set Folder FSO
Dim Folder As Scripting.Folder
Set Folder = FSO.GetFolder(FolderPath)
' Loop thru each file -> Assuming only 6 files as per you question
Dim File As Scripting.File
For Each File In Folder.Files
Dim wkbData As Workbook
Set wkbData = Workbooks.Open(File.path)
Dim wksData As Worksheet
Set wksData = wkbData.Worksheets("Sheet1") ' -> Assume this file has only 1 worksheet
Dim BlankRow As Long
BlankRow = wks.Range("A" & wks.Rows.Count).End(xlUp).Row + 1
Dim i As Long
For i = 1 To 4
wks.Cells(BlankRow, i).Value = wksData.Range(CopyRange(i)).Value
Next i
wkbData.Close False
Next File
Range("A:I").EntireColumn.AutoFit
Application.ScreenUpdating = True
End Sub
Related
This is the first week I learn vba so bear with me if I have a lot of questions;-)
So I have two folders, one folder contains the templates I need to update, the other contains the reports that the updates will be copied from. Cell A1 in each template contains the code that is specific to that BU. I need vba to find the code in the file names in the report folder and open that report. The problem is that the report names have different lengths, eg. it's named as XXX region_code_XXXXXXXXXXX, there can be any number of "X" before and after the code.
Sub Macro1()
Set fso = CreateObject("scripting.filesystemobject")
Set ff = fso.getfolder("C:\Users\35264\summary\test")
For Each file In ff.Files
Workbooks.Openfile
Set wbk2 = ActiveWorkbook
Sheets("Summary").Select
Range("A1").Select
rngX = Range("A1").Value
Now I need to find rngX in the file names in the report folder... I can't figure out how. Let me know if anyone can help! Thank you!
I am learning how to use dir function. I think it will be helpful to get the names of the reports first.
Combine the FileSystemObject Object With the Dir Function
Dir cannot be used in nested Do...Loops.
Using the FileSystemObject object, it opens files in one folder and uses the information in it to open specific files in another folder by using the Dir function. For each combination, it prints their names to the immediate window and closes each file without saving changes.
A better way to do this would be to write the file paths of the first folder to an array by using the Dir function and then loop through the elements of the array to open each file... etc.
Option Explicit
Sub PrintTemplatesAndReports()
' Templates
Const tFolderPath As String = "C:\Users\35264\summary\templates\"
Const tWorksheetName As String = "Summary"
Const rFilePatternAddress As String = "A1"
Const tFileExtensionLeft As String = "xls"
' Reports
Const rFolderPath As String = "C:\Users\35264\summary\reports\"
Const rFileExtensionPattern As String = ".xls*"
' 1st Worbook (ThisWorkbook)
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FolderExists(tFolderPath) Then Exit Sub
If Not fso.FolderExists(rFolderPath) Then Exit Sub
Dim fsoFolder As Object: Set fsoFolder = fso.Getfolder(tFolderPath)
' Templates (using the FileSystemObject object)
Dim fsoFile As Object
Dim twb As Workbook, tws As Worksheet
Dim tExtension As String, tFilePath As String
' Report (using Dir)
Dim rwb As Workbook
Dim rFilePattern As String, rFileName As String, rFilePath As String
' Counters
Dim ttCount As Long, tCount As Long, rCount As Long
For Each fsoFile In fsoFolder.Files
ttCount = ttCount + 1
tExtension = fso.GetExtensionName(fsoFile)
If InStr(1, tExtension, tFileExtensionLeft, vbTextCompare) = 1 Then
tCount = tCount + 1
tFilePath = tFolderPath & fsoFile.Name
' 2nd Workbook (Template)
Set twb = Workbooks.Open(tFilePath)
On Error Resume Next
Set tws = twb.Worksheets(tWorksheetName)
On Error GoTo 0
If Not tws Is Nothing Then
rFilePattern = CStr(tws.Range(rFilePatternAddress).Value)
rFileName = Dir(rFolderPath, "*" & rFilePattern _
& "*" & rFileExtensionPattern)
Do Until Len(rFileName) = 0
rCount = rCount + 1
rFilePath = rFolderPath & rFileName
' 3rd Workbook (Report)
Set rwb = Workbooks.Open(rFolderPath, rFileName)
' Do your thing, e.g.:
Debug.Print twb.Name, rwb.Name
rwb.Close SaveChanges:=False
rFileName = Dir ' next report
Loop
Set tws = Nothing
End If
twb.Close SaveChanges:=False
End If
Next fsoFile ' next template
MsgBox "Template files processed: " & tCount & "(" & ttCount & ")" _
& vbLf & "Report files processed: " & rCount & "(" & tCount & ")", _
vbInformation
End Sub
I have 200 sheets in 1 workbook with an average of 65,000 records on each sheet. I am trying to build a macro that merges all sheets in 1 Excel file into the minimum number of sheets on a NEW Excel file. As Excel has a limitation of 1.xxx million records, the new file would have to have more than 1 sheet, but I am looking to consolidate as much as possible on the new file/tabs.
Below is what I have built so far, but I am struggling to even copy and past the data properly, let alone adding new sheets whenever needed.
Is anyone able to assist?
Sub Combine()
Dim J As Integer
Dim s As Worksheet
Dim wb As Workbook
Set wb = ActiveWorkbook
Set ws = wb.ActiveSheet
Sheets(1).Select
'Opens initial file
strFile = Application.GetOpenFilename
Workbooks.Open strFile
Set INITIALFILE = ActiveWorkbook
' copy headings
Sheets(1).Activate
Range("A1").EntireRow.Select
Selection.Copy
wb.Sheets("Sheet1").Activate
Sheets("Sheet1").Range("A" & Sheets(1).Rows.Count).End(xlUp).Offset(0, 0).PasteSpecial
INITIALFILE.Activate
For Each s In ActiveWorkbook.Sheets
If s.Name <> "Combined" Then
Application.GoTo Sheets(s.Name).[a1]
Selection.CurrentRegion.Select
' Don't copy the headings
Selection.Offset(1, 0).Resize(Selection.Rows.Count - 1).Select
Selection.Copy
wb.Sheets("Sheet1").Activate
Sheets("Sheet1").Range("A" & Sheets(1).Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial
INITIALFILE.Activate
End If
Next
End Sub
If you use Range object variables, you rarely need to use Select and your screen will be much the quieter. As noted using a general On Error Resume Next will make it almost impossible to make your code work properly as you will not see useful error messages.
Sub Combine()
Dim NewFile As Workbook
Dim InitialFile As Workbook
Const RowLimit As Long = 1000000
Dim strFile As String
Dim InRows As Long
Dim OutRows As Long
Dim FirstSheet As Worksheet
Dim OutSheet As Worksheet
Dim ASheet As Worksheet
Dim CopySet As Range
Dim OutLoc As Range
Dim Anon As Variant
Set NewFile = ActiveWorkbook
Set FirstSheet = NewFile.Sheets.Add(After:=Sheets(Sheets.Count))
Set OutSheet = FirstSheet
Set OutLoc = OutSheet.Range("A1")
'Opens initial file
strFile = Application.GetOpenFilename
Workbooks.Open strFile
Set InitialFile = ActiveWorkbook
OutSheet.Activate
For Each ASheet In InitialFile.Sheets
Anon = DoEvents()
If ASheet.Name <> "Combined" Then
Set CopySet = ASheet.Cells.SpecialCells(xlCellTypeLastCell)
If CopySet.Row + OutLoc.Row > RowLimit Then
Set OutSheet = NewFile.Sheets.Add(After:=OutSheet)
Set OutLoc = OutSheet.Range("A1")
End If
' Only copy the headings if needed
If OutLoc.Row = 1 Then
Set CopySet = Range(ASheet.Range("A1"), CopySet)
Else
Set CopySet = Range(ASheet.Range("A2"), CopySet)
End If
CopySet.Copy OutLoc
Set OutLoc = OutLoc.Offset(CopySet.Rows.Count, 0)
End If
Next ASheet
FirstSheet.Activate
End Sub
The call to DoEvents() is there to keep the screen current rather than frozen in some half-drawn fashion.
In my folder, there is AA.bmp, AA.txt, BB.bmp and BB.txt
I am able to extract the data for AA.txt and BB.txt in a separate worksheet.
Am I also able to insert AA.bmp in the same sheet as AA.txt, and BB.bmp in the same sheet as BB.txt?
Sub ExtractData()
iPath = "C:\Users\NHWD78\Desktop\Report\Radiated Immunity\"
ifile = Dir(iPath & "*.txt")
Do While Len(ifile)
Sheets.Add , Sheets(Sheets.Count), , iPath & ifile
ifile = Dir
Range("A10:B10, A16:B19").Copy Destination:=Sheets(Sheets.Count).Range("A1")
Application.CutCopyMode = False
Range("A6:K600").Clear
Columns.AutoFit
Loop
End Sub
I have search throughout the website but only found a way to insert a fixed image with image name.
This will answer your query, its more of a solution than an answer which is not what this site is for, but take the time to read through it as it should be educationally useful too.
You are trying to parse a folder that has content similar to below:-
The result os for these to be in an Excel workbook, with a worksheet containing the text and image for each group (AA, BB, and CC)
First step I would take is to use Microsoft Scripting Runtime, this makes parsing the folder a lot easier. To enable this, within the VBA environment (known as the IDE), select 'Tools' > 'References...', scroll down to 'Microsoft Scripting Runtime' and tick it, then click 'OK' to close the dialog box.
That allows us to the File System Object, which is a very useful file and folder manipulation and interrogation feature set.
Firstly we care most about the *.txt files so lets begin by looping through them:-
Dim FSO As New FileSystemObject
Dim Fldr As Folder
Dim Fl As File
'First we set Fldr to be the folder we care about
Set Fldr = FSO.GetFolder("C:\Users\garye\Desktop\Work")
'Then start a loop to look through each file in the folder
For Each Fl In Fldr.Files
'If the files ends in .txt then we care about it (UCASE used to make it case insensitive)
If Right(UCase(Fl.Name), 4) = ".TXT" Then
'We have found a file
End If
'Do events returns the processor to the system for any other items to be process
'very useful in a loop on a Windows based machine to stop resource hogging and lock ups
DoEvents
Next
Set Fldr = Nothing
Next on the discovery of a text file we want to create a worksheet and import the text. For the sake of this example, it will all be done in a new workbook as well.
Dim WkBk As Workbook
Dim WkBk_Tmp As Workbook
Dim WkSht As Worksheet
Dim WkSht_Tmp As Worksheet
Dim StrName As String
'Create a new workbook
Set WkBk = Application.Workbooks.Add
'...
'Collect the name (i.e. AA from AA.txt)
StrName = Left(Fl.Name, Len(Fl.Name) - 4)
'Create a new worksheet in out new workbook
Set WkSht = WkBk.Worksheets.Add
'Change the worksheet name to the file name
WkSht.Name = StrName
'Open the file in Excel
Set WkBk_Tmp = Application.Workbooks.Open(Fl.Path)
Set WkSht_Tmp = WkBk_Tmp.Worksheets(1)
'Copy its contents into out worksheet
WkSht_Tmp.Cells.Copy WkSht.Cells
Set WkSht_Tmp = Nothing
'Close the file
WkBk_Tmp.Close 0
Set WkBk_Tmp = Nothing
Next we want to insert the image if it is there:-
Dim Rng As Range
'...
'See it a bmp file exists (i.e. AA.bmp)
If FSO.FileExists(Fldr.Path & "\" & StrName & ".bmp") Then
'This get the bottom row of data as a position to insert the image
Set Rng = WkSht.Range(WkSht.Range("A1").End(xlDown).Address).Next(2, 0)
'Add the picture
WkSht.Shapes.AddPicture Fldr.Path & "\" & StrName & ".bmp", msoFalse, msoCTrue, Rng.Left, Rng.Top, -1, -1
Set Rng = Nothing
End If
If we put all the above together it looks as below, hopefully this has been education in what is happening in the code, some good practices, how to approaching a task.
Option Explicit
Sub ExtractData()
Dim FSO As New FileSystemObject
Dim Fldr As Folder
Dim Fl As File
Dim WkBk As Workbook
Dim WkBk_Tmp As Workbook
Dim WkSht As Worksheet
Dim WkSht_Tmp As Worksheet
Dim StrName As String
Dim Rng As Range
'Create a new workbook
Set WkBk = Application.Workbooks.Add
'First we set Fldr to be the folder we care about
Set Fldr = FSO.GetFolder("C:\Users\garye\Desktop\Work")
'Then start a loop to look through each file in the folder
For Each Fl In Fldr.Files
'If the files ends in .txt then we care about it (UCASE used to make it case insensitive)
If Right(UCase(Fl.Name), 4) = ".TXT" Then
'Collect the name (i.e. AA from AA.txt)
StrName = Left(Fl.Name, Len(Fl.Name) - 4)
'Create a new worksheet in out new workbook
Set WkSht = WkBk.Worksheets.Add
'Change the worksheet name to the file name
WkSht.Name = StrName
'Open the file in Excel
Set WkBk_Tmp = Application.Workbooks.Open(Fl.Path)
Set WkSht_Tmp = WkBk_Tmp.Worksheets(1)
'Copy its contents into out worksheet
WkSht_Tmp.Cells.Copy WkSht.Cells
Set WkSht_Tmp = Nothing
'Close the file
WkBk_Tmp.Close 0
Set WkBk_Tmp = Nothing
'See it a bmp file exists (i.e. AA.bmp)
If FSO.FileExists(Fldr.Path & "\" & StrName & ".bmp") Then
'This get the bottom row of data as a position to insert the image
Set Rng = WkSht.Range(WkSht.Range("A1").End(xlDown).Address).Next(2, 0)
'Add the picture
WkSht.Shapes.AddPicture Fldr.Path & "\" & StrName & ".bmp", msoFalse, msoCTrue, Rng.Left, Rng.Top, -1, -1
Set Rng = Nothing
End If
Set WkSht = Nothing
End If
'Do events returns the processor to the system for any other items to be process
'very useful in a loop on a Windows based machine to stop resource hogging and lock ups
DoEvents
Next
Set Fldr = Nothing
Set WkBk = Nothing
MsgBox "Done!"
End Sub
Worksheet.Shapes.AddPicture will do it. Example below: -
Public Sub Sample()
Dim WkBk As Workbook
Dim WkSht As Worksheet
Dim Ole As Object
Set WkBk = ThisWorkbook
Set WkSht = WkBk.Worksheets(1)
WkSht.Shapes.AddPicture "C:\Users\garye\Desktop\AA.bmp", msoFalse, msoCTrue, 0, 0, -1, -1
Set WkSht = Nothing
Set WkBk = Nothing
End Sub
I've built an Excel Macro which takes the first sheets from all XLS files in a selected folder (including XLS files in any sub-folders) and copies the sheets onto a single sheet in a new Workbook. The code seems to work fine for the most part and I intend to use it to merge thousands of Excel sheets into a single file.
However the problem is that the loop just stops working at some point, with no errors raised. Sometimes it's a couple of hundred files, sometimes more. But the process seems to be unreliable and I can't tell why.
This is my code (I call the Merge macro which in turn calls the DoFolder Sub):
Sub Merge()
Dim FileSystem As Object
Dim HostFolder As String
HostFolder = "C:\XLSfiles"
Set FileSystem = CreateObject("Scripting.FileSystemObject")
DoFolder FileSystem.GetFolder(HostFolder)
End Sub
Sub DoFolder(Folder)
Dim unusedRow As Long 'used for writing the file path info before each copied sheet
Dim path As String, ThisWB As String, lngFilecounter As Long
Dim wbDest As Workbook, shtDest As Worksheet, ws As Worksheet
Dim Filename As String, Wkb As Workbook
Dim CopyRng As Range, Dest As Range
Dim RowofCopySheet As Integer
RowofCopySheet = 2 ' Row to start on in the sheets you are copying from
ThisWB = ActiveWorkbook.Name
Set shtDest = ActiveWorkbook.Sheets(1)
Dim SubFolder
For Each SubFolder In Folder.SubFolders
DoFolder SubFolder
Next
Dim File
ActiveWindow.WindowState = xlMinimized
For Each File In Folder.Files
' Operate on each file
unusedRow = Cells.SpecialCells(xlCellTypeLastCell).Offset(1, 0).Row
If Not Filename = ThisWB Then
Set Wkb = Workbooks.Open(File)
Set CopyRng = Wkb.Sheets(1).Range(Cells(RowofCopySheet, 1),Cells(ActiveSheet.UsedRange.Rows.Count, ActiveSheet.UsedRange.Columns.Count))
Set Dest = shtDest.Range("A" & shtDest.UsedRange.SpecialCells(xlCellTypeLastCell).Row + 1)
CopyRng.Copy Dest
Wkb.Close False
End If
Cells(unusedRow, 1) = File
Application.StatusBar = File
Next
Range("A1").Select
End Sub
What am I missing?
Try this:
Instead of copying ranges, you can simply set their value
Do not use Cells when working with multiple sheets; always explicitely state the object/sheet whose Cells you like to address
Sample that worked for me:
For Each fi In f.Files
If InStr(1, Right(fi.Name, 5), ".xls") > 0 Then
Set Wkb = Workbooks.Open(fi)
Set ws = Wkb.Sheets(1)
rowCount = ws.UsedRange.Rows.Count
colCount = ws.UsedRange.Columns.Count
ranString = shtDest.Cells(curRow, 1).Address & ":" & shtDest.Cells(curRow + rowCount, colCount).Address
Set ran = ws.Range(ws.Cells(2, 1).Address, ws.Cells(rowCount, colCount).Address)
Set destRan = shtDest.Range(ranString)
destRan.Value = ran.Value
curRow = curRow + rowCount
Wkb.Close False
End If
Next fi
It may look a bit long-winded building a range string first, but it made debugging easier.
I am looking to copy/paste data from workbooks within different sub-folders within a user selected directory.
To explain: Basically, I want to automate a process whereby I perform a copy/paste ritual from many workbooks into one Summary Workbook (from which the code is initiated).
I have allowed user input to select the parent directory in which the subfolders are contained.
I have reached the stage where the looping occurs, but the data is not being copied and pasted.
Can anyone see flaw in my code or why it is not working?
Your help will be greatly appreciated.
Sub AAA()
Dim FSO As Scripting.FileSystemObject
Dim FF As Scripting.Folder
Dim SubF As Scripting.Folder
Dim strFolderName As String
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
If .Show = -1 Then
strFolderName = .SelectedItems(1)
Else
MsgBox ("Selection Cancelled")
Exit Sub
End If
End With
Set FSO = New Scripting.FileSystemObject
Set FF = FSO.GetFolder(strFolderName)
For Each SubF In FF.SubFolders
DoOneFolder SubF
Next SubF
End Sub
Sub DoOneFolder(FF As Scripting.Folder)
Dim F As Scripting.File
Dim SubF As Scripting.Folder
Dim WBc As Workbook
Dim shtWBc As Object
Set shtWBc = Sheets("QC Results")
Dim shtBatchwbk As Object
Dim lastrow As Long
Set shtBatchwbk = ThisWorkbook.Sheets("QC Results")
lastrow = shtBatchwbk.Range("A65536").End(xlUp).Row
For Each F In FF.Files
If (F.Name) Like "QC_results*" & ".xlsm" Then
Set WBc = Workbooks.Open(F)
' Copy QC results range into batch summary workbook
shtWBc.Range("A4:SA11").Copy shtBatchwbk.Range("A" & lastrow)
WBc.Close SaveChanges:=False
Debug.Print F.Name
End If
Next F
For Each SubF In FF.SubFolders
DoOneFolder SubF
Next SubF
End Sub
I believe your problem is here:
Sub DoOneFolder(FF As Scripting.Folder)
Dim F As Scripting.File
Dim SubF As Scripting.Folder
Dim WBc As Workbook
Dim shtWBc As Object
Set shtWBc = Sheets("QC Results")
Dim shtBatchwbk As Object
Dim lastrow As Long
Set shtBatchwbk = ThisWorkbook.Sheets("QC Results")
lastrow = shtBatchwbk.Range("A65536").End(xlUp).Row
Notice that shtWBc and shtBatchwbk are most likely assigned to the exact same worksheet object.
shtBatchwbk is assigned from ThisWorkbook, and unless you have other workbooks open/active at the time, then shtWBc is assigned from the ActiveWorkbook, which would be the same workbook, thus the exact same worksheet.
The resolution would seem to be:
For Each F In FF.Files
If (F.Name) Like "QC_results*" & ".xlsm" Then
Set WBc = Workbooks.Open(F)
Set shtWBc = WBc.Sheets("QC Results") '##### ASSIGN THIS WORKSHEET FROM THE NEWLY OPNEED WORKBOOk
'Get the last row:
lastrow = shtBatchwbk.Range("A65536").End(xlUp).Row
' Copy QC results range into batch summary workbook
shtWBc.Range("A4:SA11").Copy shtBatchwbk.Range("A" & lastrow)
WBc.Close SaveChanges:=False
Debug.Print F.Name
End If
Next F
Otherwise, revise your Q to provide more detail per Alexandre's comment.
UPDATE FROM COMMENTS
If you only need values, then this should be more reliable and faster than copy/paste.
Instead of:
shtWBc.Range("A4:SA11").Copy shtBatchwbk.Range("A" & lastrow)
Do this:
shtBatchwbk.Range("A" & lastrow).Resize( _
shtWBc.Range("A4:SA11").Rows.Count, _
shtWBc.Range("A4:SA11").Columns.Count).Value = shtWBc.Range("A4:SA11").Value
And if you also want to copy formatting without doing the full "copy" because it sometimes changes/transposes numbers, etc., then you can add this as well:
shtWBc.Range("A4:SA11").Copy
shtBatchwbk.Range("A" & lastrow).PasteSpecial xlPasteFormats