My code is working perfectly in debug mode. I step through every single line, and it outputs exactly how I expect. However, when it runs on open, like i want it to, it doesn't execute the last 2-3 lines properly. I put a note where it stops in the code snip below. I am a very amateur coder, so please forgive the toddler level organization and efficiency. Any and all critiques or suggestions are welcome, I am really just figuring this out as i go.
Private Sub Workbook_Open()
'Message asks user if they want to update
Dim Answer As VbMsgBoxResult
Answer = MsgBox("Would you like to update the part HS database for your tracker?" & vbCrLf & "(It will take a hot minute)", vbYesNo + vbDefaultButton2, "Update Part DB")
'if check for running the subroutine
If Answer = vbYes Then
''Turn off screen updating
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Open parser
Workbooks.Open Filename:="K:\04_Classification\Broker Templates\BrokerPartsListParser.xlsb", ReadOnly:=True
'Setting Range Variables
Dim RgData As Range
Dim RgCriteria As Range
Dim RgOutput As Range
Set RgData = Workbooks("BrokerPartsListParser.xlsb").Worksheets("NewestIPExtract").Range("A1").CurrentRegion
Set RgCriteria = ThisWorkbook.Worksheets("PartDBRef").Range("J1").CurrentRegion
Set RgOutput = ThisWorkbook.Worksheets("PartDBRef").Range("A3").CurrentRegion
'Clearing previous data
RgOutput.Offset(1).ClearContents
'Doing the Advanced Filter
RgData.AdvancedFilter xlFilterCopy, RgCriteria, RgOutput
'Check to see if ACE data needs to be pulled
Dim ACEPull As Boolean: ACEPull = ThisWorkbook.Worksheets("ACE Data").Range("AO2")
If ACEPull = True Then
'setting pulled date
ThisWorkbook.Worksheets("ACE Data").Range("AK2").Value = Date
'Finding last row of old data set
Dim LastRow As Long: LastRow = ThisWorkbook.Worksheets("ACE Data").Range("AA" & Rows.Count).End(xlUp).Row
'Deleting old Data
If IsEmpty(Range("AA2").Value) = False Then
ThisWorkbook.Worksheets("ACE Data").Range("A2:AA" & LastRow).ClearContents
End If
'Setting Advanced Filter Parameters
Set RgData = Workbooks("BrokerPartsListParser.xlsb").Worksheets("Unified ACE Data").Range("A1").CurrentRegion
Set RgCriteria = ThisWorkbook.Worksheets("ACE Data").Range("AH1").CurrentRegion
Set RgOutput = ThisWorkbook.Worksheets("ACE Data").Range("A1:AA1")
'Doing the Advanced Filter
RgData.AdvancedFilter xlFilterCopy, RgCriteria, RgOutput
''THIS IS WHERE MY CODE STOPS WORKING AS I WOULD EXPECT
'Finding New Last Row
LastRow = ThisWorkbook.Worksheets("ACE Data").Range("AA" & Rows.Count).End(xlUp).Row
'Filling Formulas
If IsEmpty(Range("AA3").Value) = False Then
ThisWorkbook.Worksheets("ACE Data").Range("AB2:AD" & LastRow).FillDown
End If
End If
''close parser
Workbooks("BrokerPartsListParser.xlsb").Close SaveChanges:=False
''Turn updating back on
Application.ScreenUpdating = True
Application.DisplayAlerts = True
''Thank people for their patience
MsgBox "Part DB has been updated. Thank you for your patience :D"
Exit Sub
Else
Exit Sub
End If
End Sub
To reiterate JvdV's comment you have an implicit range reference.Try adding the workbook and worksheet referenced to the IsEmpty statement.
'Filling Formulas
If IsEmpty(ThisWorkbook.Worksheets("ACE Data").Range("AA3").Value) = False Then
ThisWorkbook.Worksheets("ACE Data").Range("AB2:AD" & LastRow).FillDown
End If
``
Related
The VBA code I have has worked perfectly on two other machines and with several other worksheets without the data reappearing. I've created a macro that takes a master spreadsheet and creates a new spreadsheet for each school listed in the table. I just got a new laptop and installed Excel 365 on it. I copied the VBA code to the new machine, but when I ran it, each new worksheet still contained the data for all the schools, not just the school for that particular file. I stepped through the code, and the schools did delete, but when it got to the part where the filter was removed from the table ws.ListObjects("Data").AutoFilter.ShowAllData, all the deleted rows reappeared. I'm stumped on why this is happening - It didn't happen on the other two machines and other iterations of the file that I've used this macro on. I don't know if it's an Excel setting or a setting on this particular master file. The other two machines - one used Excel 365, and the other Excel 2016. The data is not part of PowerPivot and is not a PowerQuery, so the data only lives in the table in the worksheet.
Here is the macro:
Dim i As Integer, wb As Workbook, schools() As Variant, schools_to_delete() As Variant
Dim ws As Worksheet, rng As Range, dt As String
schools = SchoolsInList()
dt = MonthName(Month(Now)) & " " & Year(Now)
Set wb = ActiveWorkbook
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
For i = 1 To UBound(schools)
wb.SaveCopyAs ("Galileo " & dt & " " & schools(i) & ".xlsx")
Workbooks.Open ("Galileo " & dt & " " & schools(i) & ".xlsx")
Workbooks("Galileo " & dt & " " & schools(i) & ".xlsx").Activate
Set ws = Sheets("Data")
ws.Activate
schools_to_delete = schools
schools_to_delete(i) = "x"
Set rng = ws.ListObjects("Data").DataBodyRange
With ws
.AutoFilterMode = False
ws.ListObjects("Data").Range.AutoFilter Field:=18, Criteria1:= _
Array(schools_to_delete), Operator:=xlFilterValues
ws.Range(rng.Address).SpecialCells(xlCellTypeVisible).Delete
.AutoFilterMode = False
ws.ListObjects("Data").AutoFilter.ShowAllData
End With
ActiveWorkbook.RefreshAll
Call SelectA1
ActiveWorkbook.Save
ActiveWorkbook.Close
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.DisplayAlerts = True
End Sub
Function SchoolsInList() As Variant
Dim schools() As String
Dim C As Collection
Dim r As Range
Dim i As Long
Dim last_row As Long
last_row = Cells(Rows.Count, 1).End(xlUp).Row
Set C = New Collection
On Error Resume Next
For Each r In Worksheets("Data").Range("R2:R" & last_row).Cells
C.Add r.Value, CStr(r.Value)
Next
On Error GoTo 0
ReDim A(1 To C.Count)
For i = 1 To C.Count
A(i) = C.Item(i)
Next i
SchoolsInList = A
End Function
Sub SelectA1()
Dim i As Long
For i = 1 To ActiveWorkbook.Sheets.Count
Sheets(i).Activate
ActiveWindow.ScrollColumn = 1
ActiveWindow.ScrollRow = 1
Range("A1").Select
Next i
ActiveWorkbook.Worksheets(2).Activate
End Sub
I found the problem - the .AutoFilterMode = False didn't actually clear the filters that had already been placed on the table in question. The visible data WAS deleted, but the data that was filtered before the macro was run remained, and when the ws.ListObjects("Data").AutoFilter.ShowAllData ran, it cleared the previous filter, showing the rows that had been filtered before. I added the .ShowAllData code to the beginning of the With statement to avoid the same problem at a future date.
I need to create copies of entire workbooks (as there are other sheets, formatting, etc. I want to preserve) and then delete out rows of data that do not equal the current cl.value. The column headers will always be in row 1. The worksheet can have a varying amount of columns (i.e. A:D, A:F, A:G, etc.) and the end user can select any column to split by.
Referencing a cell works but if try to make it dynamic (based on user selection mentioned above) in the following part of the code:
Workbooks.Open Filename:=FName
'Delete Rows
'REFERENCING ACTUAL CELL WORKS
'Range("A1").AutoFilter 1, "<>" & cl.Value
'BELOW DOES NOT WORK
Range(ColHead).AutoFilter 1, "<>" & cl.Value
I get a
Run-time error '1004': Method 'Range' of object'_Global' Failed
Full Code Below:
Sub DisplayUserFormSplitWb()
UserFormSplitWb.Show
End Sub
Private Sub BtnOK_Click()
Call SplitWbMaster.SplitWbToFiles
End Sub
Private Sub UserForm_Initialize()
Dim SplitOptions As Range
Set SplitOptions = ActiveSheet.Range("A1", ActiveSheet.Range("A1").End(xlToRight))
SplitWbCol.List = Application.Transpose(SplitOptions.Value)
End Sub
Sub SplitWbToFiles()
Dim cl As Range
Dim OrigWs As Worksheet
Dim Subtitle As String
Dim ColValue As String
Dim ColStr As String
Dim ColNum As Long
Set OrigWs = ActiveSheet
ColValue = UserFormSplitWb.SplitWbCol.Value
Set ColHead = Rows(1).Find(What:=ColValue, LookAt:=xlWhole)
Set OffCol = ColHead.Offset(1, 0)
ColStr = Split(ColHead.Address, "$")(1)
ColNum = ColHead.Column
If OrigWs.FilterMode Then OrigWs.ShowAllData
With CreateObject("scripting.dictionary")
For Each cl In OrigWs.Range(OffCol, OrigWs.Range(ColStr & Rows.Count).End(xlUp))
If Not .exists(cl.Value) Then
.Add cl.Value, Nothing
'Turn off screen and alerts
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Create workbook copy
FPath = "U:\"
Subtitle = UserFormSplitWb.SplitWbSubtitle.Value
FName = FPath & cl.Value & "_" & Subtitle & ".xlsx"
ActiveWorkbook.SaveCopyAs Filename:=FName
Workbooks.Open Filename:=FName
'Delete Rows
'REFERENCING ACTUAL CELL WORKS
'Range("A1").AutoFilter 1, "<>" & cl.Value
'BELOW DOES NOT WORK
Range(ColHead).AutoFilter 1, "<>" & cl.Value
ActiveSheet.ListObjects(1).DataBodyRange.Delete
Range(ColHead).AutoFilter
Range(ColHead).AutoFilter
'Rename sheet
ActiveSheet.Name = Left(cl.Value, 31)
'Refresh save and close
ActiveWorkbook.RefreshAll
ActiveWorkbook.Save
ActiveWorkbook.Close False
End If
Next cl
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
MsgBox "Splitting is complete. Please check your Computer (U:) drive.", vbOKOnly, "Run Macro"
End Sub
To anyone who might stumble upon this question -
I have found that using the below code solves my issue:
ActiveSheet.Range("A1", ActiveSheet.Range("A1").End(xlToRight)).AutoFilter ColNum, "<>" & cl.Value
where:
Dim ColNum As Long
ColNum = ColHead.Column
I am working on automating an excel model by copying data from other sheets into a masterfile. I have a bit of an issue that after adding the code the file went from 25mb to 60mb, without changing the content, only adding the code. Below you can find a snippet of how I automated the imports
Sub copytest() 'Procedure for retrieving data from the sourcefiles
Dim wbTarget, wbSource As Workbook
Dim target As Object
Dim pathSource, fileName As String
Dim xlApp As Application
Dim lastRow As Long
Application.EnableEvents = False
Application.ScreenUpdating = False
'path where the data source folders are located (please keep all of them in the same directory)
pathSource = "C:\Users\vferraz\Desktop\crm stock\RAPOARTE IMPORTANTE\18.02\Rapoarte pentru Handsets\"
Set wbTarget = ThisWorkbook
Set xlApp = CreateObject("Excel.Application")
xlApp.DisplayAlerts = False
Application.CutCopyMode = False
'Stock 0001
Set wbSource = xlApp.Workbooks.Open(pathSource & "Stoc 0001.xls")
wbSource.Sheets(1).UsedRange.Copy
wbSource.Close
Set target = wbTarget.Sheets("Stock 0001")
target.UsedRange.Clear
Range("A1").Select
target.Paste
xlApp.Quit
Set wbSource = Nothing
Set xlApp = Nothing
ThisWorkbook.Sheets("Mastersheet").Activate
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
In the snippet above I only added the parsing of one file (Stock 0001), but the same method is done for other 10-15 files.
Does anyone have any ideas to improve the efficiency/size of this file based on this procedure?
P.S. Im aware that the "Paste" method might be adding formats rather than values only, then I tried adding .PasteSpecial xlPasteValues instead of paste but it eventually throw errors that I couldn't identify
Update:
Based on this solution, this is the new version I tried:
Stock 0001
Set wbSource = xlApp.Workbooks.Open(pathSource & "Stoc 0001.xls")
lastRow = wbSource.Sheets(1).Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
wbTarget.Sheets("Stock 0001").Cells.Clear
wbSource.Sheets(1).Range("A1:C" & lastRow).Copy Destination:=wbTarget.Sheets("Stock 0001").Range("A1")
wbSource.Clo
The line wbSource.Sheets(1).Range("A1:C" & lastRow).Copy Destination:=wbTarget.Sheets("Stock 0001").Range("A1" Throws the "copy method of range class failed error.
Instead of this
'Stock 0001
Set wbSource = xlApp.Workbooks.Open(pathSource & "Stoc 0001.xls")
wbSource.Sheets(1).UsedRange.Copy
wbSource.Close
Set target = wbTarget.Sheets("Stock 0001")
target.UsedRange.Clear
Range("A1").Select
target.Paste
Try this
wbSource.Sheets(1).Columns("").Copy Destination:=wbTarget.Sheets("Stock 0001").Range("A1")
Where I've put Columns just replace this with whatever range you are using via Range() or Cells etc
Copy and Paste takes a while, and has issues if you are already copying something in another location. This just takes the data for you
Also, this piece of code will be your friend forever
With Sheets("Sheet1")
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
End With
This finds the bottom row of Column A (or whatever your "always populated" column will be
Sub LastRow()
Dim wb As Workbook, ws As Worksheet, LastRow As Long
Set wb = ThisWorkbook
Set ws = Worksheets("Data")
LastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
With ws.Range(ws.Cells(2, 13), ws.Cells(LastRow, 13))
'This is Range M2:M(bottom)
.
.
'etc
.
End With
End Sub
Edit....3:
Set xlApp = CreateObject("Excel.Application")
xlApp.DisplayAlerts = False
Application.CutCopyMode = False
'Stock 0001
Set wbSource = xlApp.Workbooks.Open(pathSource & "Stock 0001.xls")
Instead of all this, please use
Set wbSource = Workbooks.Open(pathSource & "Stock 0001.xls")
You also need error handling in your code. When it breaks (file doesn't exist, path is invalid, sheet doesn't exist) between
Application.EnableEvents = False
Application.ScreenUpdating = False
and
Application.EnableEvents = True
Application.ScreenUpdating = True
you're going to end up with Excel in a bad state where screen updating is off and events will no longer fire. What you should have is something long the lines of
On Error GoTo ExitErr
Application.EnableEvents = False
Application.ScreenUpdating = False
Then after your code, you should have
ExitErr:
Application.EnableEvents = True
Application.ScreenUpdating = True
I found a way to reduce the file size back to how it used to be by adding the following line to the imports after the paste command
target.Cells.ClearFormats
In this case the formats taken from the data were cleared.
I am working on a VBA project, that requires update of a specific table via power query as part of the code.
The code power query refresh needs to finish, before the query continues, but, i have not managed to find a solution to do that yet.
Option Explicit
Option Base 1
Public Sub LoadProductsForecast()
I have inserted a couple steps to optimise performance
'Deactivate global application parameters to optimise code performance
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayStatusBar = False
'Dimensions used in code for sheets etc.
Dim lastrow As Integer
Dim NoRowsInitial As Integer
''''''''''''''''''''''
''Get product data, and copy index match formula to look up the forecast
' find number of rows to use for clearing
NoRowsInitial = WorksheetFunction.CountA(Worksheets("Monthly Forecast").Range("D4:D15000"))
'Selecting Worksheet w. product master data
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ActiveWorkbook
Set ws = Sheets("Products")
wb.Activate
ws.Select
The next line is where I wish to refresh the power query, and the refresh part works as it should.
However, it countinues to run the next VBA code. I have searched for different answers online, and some refer to "DoEvents", however, it does not seem to make a difference.
ActiveWorkbook.Connections("Query - tblAdjustments").Refresh
DoEvents
Below, is the remaining code that should run after the PowerQuery has refreshed the table:
'Calculating number of rows to copy
lastrow = WorksheetFunction.CountA(Worksheets("Products").Range("B4:B15000"))
'Copying rows
Worksheets("Products").Range(Cells(4, 2), Cells(lastrow + 3, 10)).Copy
'Selecring forecast sheet
Set ws = Sheets("Monthly Forecast")
ws.Select
'Disabling alerts, so pop up for pasting data does not show (activated again later)
Application.DisplayAlerts = False
'Pasting product master data
Worksheets("Monthly Forecast").Range(Cells(8, 4), Cells(lastrow, 12)).PasteSpecial
'Creating a string that contains range to paste formula in to
Dim RangeString As String
RangeString = "N8:W" & lastrow + 7
'Copying formula to paste
Range("AJ2:AJ3").Select
Selection.Copy
'Pasting formula that looks up baseline FC (both seasonal and SES)
Range(RangeString).Select
ActiveSheet.Paste
Calculate
With Range(RangeString)
.Value = .Value
End With
'Activating alerts again
Application.DisplayAlerts = True
''''''''''''''''''''''
''Code to clean the rows that are not used
'Remove unescessary rows
Dim NPIProducts As Integer
NPIProducts = [tblNewProd].Rows.Count
'tbl.Range.Rows.Count
Dim RowsToDelete As String
RowsToDelete = lastrow + NPIProducts * 2 & ":" & NoRowsInitial
If Left(RowsToDelete, 1) = "-" Then
'do nothing (negative)
Else
[tblMonthly].Rows(RowsToDelete).Delete
End If
'''''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''''
''''End of main code
'Activate global application parameters again
Application.EnableEvents = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.DisplayStatusBar = True
'Messages end user that the calculation is done
MsgBox "Load of products and forecast finished"
End Sub
If your connection is OLEDB or ODBC you can set the background refresh temporarily to false - forcing the refresh to happen before code can continue on. Instead of calling
.Connections("Query - tblAdjustments").Refresh
do something like this:
Dim bRfresh As Boolean
With ThisWorkbook.Connections("Query - tblAdjustments").OLEDBConnection
bRfresh = .BackgroundQuery
.BackgroundQuery = False
.Refresh
.BackgroundQuery = bRfresh
End With
this example assumes you have an OLEDB connection. If you had ODBC, just replace OLEDBConnection with ODBCConnection
If you haven't already, disable background refresh for the query (plus any queries that precede that query in the evaluation chain).
You'll want to make sure that the background refresh option is not ticked. I accessed this window by right-clicking the query and then clicking Properties. I think in some other Excel versions, you might instead need to go to Data > Connections, find the query in the list and then edit its properties there.
This is untested but in theory it should work.
Split your code in two parts.
The first part ends with the refresh.
sub some_sub()
'Deactivate global application parameters to optimise code performance
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayStatusBar = False
'Dimensions used in code for sheets etc.
Dim lastrow As Integer
Dim NoRowsInitial As Integer
''''''''''''''''''''''
''Get product data, and copy index match formula to look up the forecast
' find number of rows to use for clearing
NoRowsInitial = WorksheetFunction.CountA(Worksheets("Monthly Forecast").Range("D4:D15000"))
'Selecting Worksheet w. product master data
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ActiveWorkbook
Set ws = Sheets("Products")
wb.Activate
ws.Select
ActiveWorkbook.Connections("Query - tblAdjustments").Refresh
end sub
Then in order to wait for it to finnish we let the sub run to end.
Then we let Excel fire the Worksheet_Change.
On the sheet:
Private Sub Worksheet_Change(ByVal Target As Range)
'Calculating number of rows to copy
lastrow = WorksheetFunction.CountA(Worksheets("Products").Range("B4:B15000"))
'Copying rows
Worksheets("Products").Range(Cells(4, 2), Cells(lastrow + 3, 10)).Copy
'Selecring forecast sheet
Set ws = Sheets("Monthly Forecast")
ws.Select
'Disabling alerts, so pop up for pasting data does not show (activated again later)
Application.DisplayAlerts = False
'Pasting product master data
Worksheets("Monthly Forecast").Range(Cells(8, 4), Cells(lastrow, 12)).PasteSpecial
'Creating a string that contains range to paste formula in to
Dim RangeString As String
RangeString = "N8:W" & lastrow + 7
'Copying formula to paste
Range("AJ2:AJ3").Select
Selection.Copy
'Pasting formula that looks up baseline FC (both seasonal and SES)
Range(RangeString).Select
ActiveSheet.Paste
Calculate
With Range(RangeString)
.Value = .Value
End With
'Activating alerts again
Application.DisplayAlerts = True
''''''''''''''''''''''
''Code to clean the rows that are not used
'Remove unescessary rows
Dim NPIProducts As Integer
NPIProducts = [tblNewProd].Rows.Count
'tbl.Range.Rows.Count
Dim RowsToDelete As String
RowsToDelete = lastrow + NPIProducts * 2 & ":" & NoRowsInitial
If Left(RowsToDelete, 1) = "-" Then
'do nothing (negative)
Else
[tblMonthly].Rows(RowsToDelete).Delete
End If
'''''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''''
''''End of main code
'Activate global application parameters again
Application.EnableEvents = True
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.DisplayStatusBar = True
'Messages end user that the calculation is done
MsgBox "Load of products and forecast finished"
End Sub
You can use Target to not make it run if you don't want to. I assume there is at least one cell that you know will change. Set the target there.
I have an excel file with complete mixture of data (column 1, Name). I want to split the data into multiple sheets in the same workbook based on the first column i.e., Name. I found solution to this in VBA but I want this is VB Script. Please help. Thanks in advance.
`Sub parse_data()
Dim lr As Long
Dim ws As Worksheet
Dim vcol, i As Integer
Dim icol As Long
Dim myarr As Variant
Dim title As String
Dim titlerow As Integer
vcol = 1
Set ws = Sheets("ZPC_STATS")
lr = ws.Cells(ws.Rows.Count, vcol).End(xlUp).Row
title = "A1:G1"
titlerow = ws.Range(title).Cells(1).Row
icol = ws.Columns.Count
ws.Cells(1, icol) = "Unique"
For i = 2 To lr
On Error Resume Next
If ws.Cells(i, vcol) <> "" And Application.WorksheetFunction.Match(ws.Cells(i, vcol), ws.Columns(icol), 0) = 0 Then
ws.Cells(ws.Rows.Count, icol).End(xlUp).Offset(1) = ws.Cells(i, vcol)
End If
Next
myarr = Application.WorksheetFunction.Transpose(ws.Columns(icol).SpecialCells(xlCellTypeConstants))
ws.Columns(icol).Clear
For i = 2 To UBound(myarr)
ws.Range(title).AutoFilter field:=vcol, Criteria1:=myarr(i) & ""
If Not Evaluate("=ISREF('" & myarr(i) & "'!A1)") Then
Sheets.Add(after:=Worksheets(Worksheets.Count)).Name = myarr(i) & ""
Else
Sheets(myarr(i) & "").Move after:=Worksheets(Worksheets.Count)
End If
ws.Range("A" & titlerow & ":A" & lr).EntireRow.Copy Sheets(myarr(i) & "").Range("A1")
Sheets(myarr(i) & "").Columns.AutoFit
Next
ws.AutoFilterMode = False
ws.Activate
End Sub`
You could rewrite the whole code in VBS. However, if it is only for calling the Excel Macro "invisible", the following script may be an alternative. You keep your code within Excel and just call it from VBScript:
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = false
objExcel.Application.Run "'C:\test1.xlsm'!module1.testSub"
objExcel.DisplayAlerts = False
objExcel.Application.Quit
Set objExcel = Nothing
I would recommend to set the visible-attribute to false only after testing that everything works. Note that this script closes excel without saving anything, so you have to add the save-command into your VBA code.
Update
If you really want to put everything into VBS:
remove all types from your variable definitions - basically all
variables in VBS are of type variant.
Declare all Excel-Constants you need (because VBS doesn't know them)
Use the Excel object If you
need methods of the application object (eg evaluate)
VBScript doesn't support named arguments for function or subroutine calls, so
you have to change the syntax there.
This fragment can give you an idea:
option explicit
const xlDown = -4121
const xlUp = -4162
dim objExcel
Set objExcel = CreateObject("Excel.Application")
' objExcel.Visible = false
dim wb, ws
set wb = objExcel.Workbooks.open("C:\Test1.xlsm")
with wb
Set ws = .Sheets("Sheet1")
dim lastRow
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
' ...
dim newSheet
set newSheet = .Sheets.Add(, .Worksheets(.Worksheets.Count))
newSheet.name = myarr(i)
' ...
end with
' ...
wb.close true ' At the end, do not forget to save the work
set wb = nothing
Set objExcel = Nothing
Important: If your script fails half the way (and it will during development), make sure that the Excel Instance is closed. Check the task manager if there are suspicious instances of Microsoft Excel running. Also, if at the end of the work a SaveAs-Dialog pops up, it is likely that an previous instance of Excel didn't terminate and now the file was opened in ReadOnly mode. It is not possible to add a On error goto CleanUp to enforce the closure of excel, see https://stackoverflow.com/a/157785/7599798
Try it like this.
In the code you see four filter examples that you can use, we use example 1 in this macro and I commented the other 3 examples in the code.
1: Criteria in the code (=Netherlands, see the tips below the macro)
2: Filter on ActiveCell value
3: Filter on Range value (D1 in this example)
4: Filter on InputBox value
Sub Copy_With_AutoFilter1()
'Note: This macro use the function LastRow
Dim My_Range As Range
Dim CalcMode As Long
Dim ViewMode As Long
Dim FilterCriteria As String
Dim CCount As Long
Dim WSNew As Worksheet
Dim sheetName As String
Dim rng As Range
'Set filter range on ActiveSheet: A1 is the top left cell of your filter range
'and the header of the first column, D is the last column in the filter range.
'You can also add the sheet name to the code like this :
'Worksheets("Sheet1").Range("A1:D" & LastRow(Worksheets("Sheet1")))
'No need that the sheet is active then when you run the macro when you use this.
Set My_Range = Range("A1:D" & LastRow(ActiveSheet))
My_Range.Parent.Select
If ActiveWorkbook.ProtectStructure = True Or _
My_Range.Parent.ProtectContents = True Then
MsgBox "Sorry, not working when the workbook or worksheet is protected", _
vbOKOnly, "Copy to new worksheet"
Exit Sub
End If
'Change ScreenUpdating, Calculation, EnableEvents, ....
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
ActiveSheet.DisplayPageBreaks = False
'Firstly, remove the AutoFilter
My_Range.Parent.AutoFilterMode = False
'Filter and set the filter field and the filter criteria :
'This example filter on the first column in the range (change the field if needed)
'In this case the range starts in A so Field 1 is column A, 2 = column B, ......
'Use "<>Netherlands" as criteria if you want the opposite
My_Range.AutoFilter Field:=1, Criteria1:="=Netherlands"
'If you want to filter on a cell value you can use this, use "<>" for the opposite
'This example uses the activecell value
'My_Range.AutoFilter Field:=1, Criteria1:="=" & ActiveCell.Value
'This will use the cell value from A2 as criteria
'My_Range.AutoFilter Field:=1, Criteria1:="=" & Range("A2").Value
''If you want to filter on a Inputbox value use this
'FilterCriteria = InputBox("What text do you want to filter on?", _
' "Enter the filter item.")
'My_Range.AutoFilter Field:=1, Criteria1:="=" & FilterCriteria
'Check if there are not more then 8192 areas(limit of areas that Excel can copy)
CCount = 0
On Error Resume Next
CCount = My_Range.Columns(1).SpecialCells(xlCellTypeVisible).Areas(1).Cells.Count
On Error GoTo 0
If CCount = 0 Then
MsgBox "There are more than 8192 areas:" _
& vbNewLine & "It is not possible to copy the visible data." _
& vbNewLine & "Tip: Sort your data before you use this macro.", _
vbOKOnly, "Copy to worksheet"
Else
'Add a new Worksheet
Set WSNew = Worksheets.Add(After:=Sheets(ActiveSheet.Index))
'Ask for the Worksheet name
sheetName = InputBox("What is the name of the new worksheet?", _
"Name the New Sheet")
On Error Resume Next
WSNew.Name = sheetName
If Err.Number > 0 Then
MsgBox "Change the name of sheet : " & WSNew.Name & _
" manually after the macro is ready. The sheet name" & _
" you fill in already exists or you use characters" & _
" that are not allowed in a sheet name."
Err.Clear
End If
On Error GoTo 0
'Copy/paste the visible data to the new worksheet
My_Range.Parent.AutoFilter.Range.Copy
With WSNew.Range("A1")
' Paste:=8 will copy the columnwidth in Excel 2000 and higher
' Remove this line if you use Excel 97
.PasteSpecial Paste:=8
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
.Select
End With
' If you want to delete the rows that you copy, also use this
' With My_Range.Parent.AutoFilter.Range
' On Error Resume Next
' Set rng = .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count) _
' .SpecialCells(xlCellTypeVisible)
' On Error GoTo 0
' If Not rng Is Nothing Then rng.EntireRow.Delete
' End With
End If
'Close AutoFilter
My_Range.Parent.AutoFilterMode = False
'Restore ScreenUpdating, Calculation, EnableEvents, ....
My_Range.Parent.Select
ActiveWindow.View = ViewMode
If Not WSNew Is Nothing Then WSNew.Select
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
https://www.rondebruin.nl/win/s3/win006_1.htm