Convert Subroutines to Functions - excel

I posted a question here, regarding using a macro and having a screentip and I received a very good idea from #Tim Williams but in order for this to work I need to convert subs into functions. I have never done that before and I don't know if it is possible but I hope one of you experts can guide me. Here are my subroutines that need to be converted for this to work.
Normal / Editable View - This program is run from full screen mode. So this subroutine allows the user to press a button and go to normal excel view.
```
Sub ScreenDisplayNorm()
Call UnProtectAllSheets
With Application
.DisplayFullScreen = False
With ActiveWindow
.WindowState = xlNormal
.DisplayHeadings = True
.DisplayWorkbookTabs = True
.DisplayGridlines = False
.DisplayHorizontalScrollBar = True
.DisplayVerticalScrollBar = True
.Zoom = 80
End With
.DisplayStatusBar = True
.DisplayFormulaBar = True
End With
End Sub
```
Fullscreen View - Press button to convert view back to fullscreen!
```
Sub GotoResources()
Set wbPB = PokerBros
Dim maxWidth As Long
Dim myWidth As Long
Dim Myzoom As Single
Dim wsR As Worksheet: Set wsR = wbPB.Worksheets("Resource Data")
Dim Rng As Range: Set Rng = wsR.Range("A1:U35")
Call UnProtectAllSheets
wsR.Activate
Call EnhancePerformance 'call sub to Improve speed (screen updates and calculations)
Call ScreenDisplayMax 'call sub to maximize
maxWidth = GetSystemMetrics(0) * 0.96 'maximum width measurement
myWidth = ActiveSheet.Range("U1").Left 'Actual width of program starting from the left edge of the cell furthest_
'to the right that fits the best view
Myzoom = maxWidth / myWidth
ActiveWindow.Zoom = Myzoom * 90
Rng.Select
ActiveWindow.Zoom = True 'set range zoom
ActiveSheet.Range("A1").Select
Call NormalPerformance 'call sub to perform normal (screen updates and calculations)
Call ProtectAllSheets
End Sub
```
Save - Button will save current file
```
Sub SaveWorkbook()
ActiveWorkbook.Save
End Sub
```
SaveAs - User can save file to a different location.
```
Sub SaveAsDialogBox()
Dim CarryOn As Boolean
Call UnProtectAllSheets
CarryOn = MsgBox("Do you want to save a copy of this original file?", vbYesNo, "Save Copy Recommended")
If CarryOn = True Then
CopyToNewBook
End If
End Sub
```
Player Profile - Opens player profile userform to gather data.
```
Sub OpenProfileUF()
ufDirectory.Show vbModeless
End Sub
```
Import Data - Allows user to calculate results
```
Sub UpdateArray()
Dim wsDest As Worksheet
Dim arSource() As Variant, arDest() As Variant, varID As Variant, ImportDate As Variant
Dim outputColumns As Variant, inputColumns As Variant
Dim filename As String: filename = "C:\Users\AdamRhodes\OneDrive\Desktop\PokerBros\PokerBrosSuppliment.xlsm"
Dim strDest As String, lastsrcrow As Long, AddRow As Integer, strfilename As String
Dim lrow As Long, CarryOn As Boolean, MsgAnswer As Integer
Set wbPB = PokerBros
Set wsPT = wbPB.Worksheets("Player Tracking")
Call UnProtectAllSheets
Call EnhancePerformance
CarryOn = MsgBox("Running this macro will import" & "," & "extract" & "," & _
"and compute data from other files and will add calculations to some reporting" & "." & "It is recommended to save a copy to restore with confidence" & "." & "If you already saved a copy and wish to proceed select ""Yes"" and select ""No"" to exit and save a copy" & "!", vbYesNo, "Please Approve Data Load")
If CarryOn = vbNo Then
Exit Sub
End If
MsgAnswer = MsgBox("Would you like to select your filepath? If you select ""NO"" The application will attempt to open the file" & ".", vbYesNoCancel + vbQuestion, "Locate File to Export Data!")
If MsgAnswer = vbYes Then
strfilename = Application.GetOpenFilename(Title:="Browse and Select you Newest PokerBrosSuppliment.xls file", FileFilter:="Excel Files (*.xls*),*xls*")
Set wbSource = Application.Workbooks.Open(filename:=strfilename, ReadOnly:=True)
ElseIf MsgAnswer = vbNo Then
On Error Resume Next 'Call function to check if the file is open
If IsFileOpen(filename) = False Then 'Insert actions to be performed on the closed file
Set wbSource = Application.Workbooks.Open(filename:=filename, ReadOnly:=True)
Else: MsgBox filename & " is already open." 'The file is open or another error occurred
Set wbSource = Workbooks("PokerBrosSuppliment.xlsm") 'open the file manually
If IsError(Error) Then 'if there is an error opening the file it may already be open.
strfilename = Application.GetOpenFilename(Title:="Browse and Select you Newest PokerBrosSuppliment.xls file", FileFilter:="Excel Files (*.xls*),*xls*")
Set wbSource = Application.Workbooks.Open(filename:=strfilename, ReadOnly:=True)
Else: Set wbSource = Application.Workbooks.Open(filename:=strfilename, ReadOnly:=True)
End If
End If
ElseIf MsgAnswer = vbCancel Then
MsgBox "No file has been selected" & "." & "File has not been imported" & "!", vbOKOnly + vbExclamation, "Procedure was Cancelled"
Exit Sub
End If
On Error GoTo 0
Set wsSource = wbSource.Worksheets("Export")
'use last row from suppliment to determine how many formatted rows needed for player tracking
lastsrcrow = wsSource.Cells(wsSource.Rows.Count, "B").End(xlUp).Row
AddRow = lastsrcrow - rCntr 'rcntr is a constant 150. so the last row minus 150 determines how many formatted rows to be added.
wsPT.Activate ' activate Player Tracking
Call AddNew_Worksheet 'call sub to add new worksheet
strDest = wbPB.Worksheets(ActiveSheet.Name).Name 'rename the new spreadsheet
Set wsDest = wbPB.Worksheets(strDest) 'set the worksheet object
If AddRow > 0 Then 'Add rows if needed
wsDest.Rows(rCntr + 1 & ":" & AddRow + rCntr + 1).Select 'determine how many rows to add
Selection.EntireRow.Insert Shift:=xlShiftDown, CopyOrigin:=xlFormatFromLeftOrAbove 'select the range and insert rows
With wsDest
.Range("B150:N150").AutoFill Destination:=Range("B150:N" & AddRow + 151), Type:=xlFillDefault 'autofill from cells above to format
End With
End If
inputColumns = Array(6, 4, 5, 7, 8, 23, 35, 36) 'source array data
outputColumns = Array(2, 3, 4, 5, 6, 7, 12, 13) 'Destination array data
Call writeArray(inputColumns, outputColumns) 'write the array
Call Get_RakeBack_Rates 'call sub to insert Rake Back Rates
Application.DisplayAlerts = False 'hide alert about closing the source file with data on the clipboard
wbSource.Close savechanges:=False 'close workbook
Application.DisplayAlerts = True 'activate alerts again
Call NormalPerformance
Call ProtectAllSheets
End Sub
```
Directory Adds - Adds new accounts to the directory.
```
Sub DirectoryAdds()
Set wbPB = PokerBros
Dim srcColumns As Variant: srcColumns = Array(2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 14)
Dim tgtColumns As Variant: tgtColumns = Array(2, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12)
Dim wsIT As Worksheet: Set wsIT = wbPB.Worksheets(Worksheets.Count)
Dim wsPD As Worksheet: Set wsPD = wbPB.Worksheets("Player Directory")
Dim Rng As Range, rngSource As Range, c As Range
Dim Curr, ub As Long, i As Long, k As Long
Dim wsPDlastrow As Long: wsPDlastrow = wsPD.Cells(Rows.Count, "B").End(xlUp).Row
Dim lastrow As Long, inc As Integer, FillRow As Integer, NextRow As Integer, lastRR As Long
Call UnProtectAllSheets
Call EnhancePerformance
ub = UBound(srcColumns) 'Player Tracking UBound column count
If wsIT Is wsPD Then 'If Player Directory sheet is Activesheet exit sub. Player Tracking needs to be active at start of macro
MsgBox "Wrong sheet selected."
Exit Sub
End If
'set source range from Player Tracking
Set rngSource = wsIT.Range(wsIT.Cells(srcFirstRow, srcColumns(0)), wsIT.Cells(Rows.Count, srcColumns(0)).End(xlUp))
For Each c In rngSource.Cells
If Len(c.Value) > 0 Then
'Simpler to search full column, but assumes there will be no match in the header or the cells above it.
Curr = Application.Match(c.Value, wsPD.Columns(tgtColumns(0)), 0) 'no Worksheetfunction=no runtime error if no match
If Not IsError(Curr) Then 'increment last column
For inc = 5 To 10
If inc > 10 Then Exit For
If inc = 6 Or inc = 7 Then
GoTo SKIP_ITERATION:
End If
With wsPD.Cells(Curr, tgtColumns(inc))
.Value = .Value + wsIT.Cells(c.Row, srcColumns(inc)).Value
End With
SKIP_ITERATION: Next inc
Else
Set Rng = wsPD.Cells(Rows.Count, tgtColumns(0)).End(xlUp).Offset(1, 0) 'no
match: copy over
For k = 0 To ub
wsPD.Cells(Rng.Row, tgtColumns(k)).Value = wsIT.Cells(c.Row, srcColumns(k)).Value
Next k
End If
End If
lastrow = wsPD.Range("B" & Rows.Count).End(xlUp).Row
With wsPD.Cells(lastrow + 1, 2)
.EntireRow.Copy
.EntireRow.Insert Shift:=xlShiftDown, CopyOrigin:=xlFormatFromLeftOrAbove
End With
Application.CutCopyMode = False
Next c
For Each c In wsPD.Range("B4:M" & lastrow) 'locate all blank rows in player details cells
If IsEmpty(c) Then
c.Value = "None" 'Put "TBD" in all blank cells
End If
Next c
MsgBox "Operation finished successfully."
Call NormalPerformance
Call ProtectAllSheets
End Sub
```

To convert a Sub to a Function, you simply need to change the definition from Sub xxx to Function xxx, the closing End Sub to End Function and any Exit Sub to Exit Function. That`s all - however it will be rather useless to do so.
None of these functions will return anything, and the basic idea of a function is to return something (in opposite to a sub that cannot return anything - in other programming languages this is called a void function). The question is why you want to convert it to a function and what you want to return?
To let a function return anything, you simply assign a value to the function name. So if you have a function foo, you write:
Function foo
(...)
foo = "Hello world"
end Function
Now, the function will return the string "Hello world". You should declare the type that is returned by the function (else it will return a variant):
Function doubleMe(x as Long) As Long
(...)
doubleMe = x * 2
End Function
and use the result on your code
myVal = doubleMe(12345)
y1 = 555
Debug.Print doubleMe(y1) & " is twice as much as " & y1
If you are not interested in the result you can call the function like any subroutine, either with Call (and use parenthesis for the parameter) or without (then you have to omit the parenthesis)
doubleMe 0
Call doubleMe(-1)
Often you see that a function returns a Boolean indicating the success
Function ToSomethingComplicated(p1, p2, p3) as boolean
ToSomethingComplicated = False
(do your complicated stuff here)
if (something failed) Then Exit Function
(do some more complicated stuff)
(...)
' If everything was successfull, return True
ToSomethingComplicated = True
End Function

Related

I get error 9 subscript out of range when ever i try to run this "file match" code

I am very new to Vba and coding in general, so please excuse any mistake I may have made. I keep getting a run time error "subscript out of range". This code is meant to work in conjunction with the first module that I wrote, which works fine, but I need to verify that this works first before I integrate it. This code is meant to find what is referred to as an "h-number", then match that h number to a list generated by the first module, then it will copy certain dates corresponding to the h number back to the current workbook (I have a place holder code for the time being). My Code:
Sub filematcher()
Dim Activesource As Workbook 'saves file so that it can be refrenced in code
Dim AOI2 As Range
Call OptimizeCode_Begin
'opens the file
Set Activesource = Workbooks.Open(Filename:="C:\Users\ebecerra\Desktop\active status sheet
copy for macro.xlsx", ReadOnly:=True)
Set AOI2 = Activesource.Sheets("Sheet 1").Range("A")
For Each cell In AOI2
On Error Resume Next
H_source = cell.Value
'matches the H number found to the one on the master sheet
With ThisWorkbook.Sheets("Collated Data").Range("A")
Set H_match = .Find(H_source, LookIn:=xlValues, lookat:=xlPart, SearchDirection:=xlNext, MatchCase:=False)
On Error Resume Next
End With
'find the row number that coresponds to the found h-number
Y_Match = H_match.Row
'records the found h number to verify that code works
ThisWorkbook.Sheets("Collated Data").Cells(Y_Match, H).Value = H_source
Next cell
Activesource.Close savechanges:=False
On Error GoTo ErrorHandle_2
ErrorHandle_2:
Set Activesource = Nothing
'End optimizer
Call OptimizeCode_End
End Sub
IF it matters this is the code optimizer as well as the first module that I mentioned:
Public CalcState As Long
Public EventState As Boolean
Public PageBreakState As Boolean
Sub OptimizeCode_Begin()
Application.ScreenUpdating = False
EventState = Application.EnableEvents
Application.EnableEvents = False
CalcState = Application.Calculation
Application.Calculation = xlCalculationManual
PageBreakState = ActiveSheet.DisplayPageBreaks
ActiveSheet.DisplayPageBreaks = False
End Sub
Sub OptimizeCode_End()
ActiveSheet.DisplayPageBreaks = PageBreakState
Application.Calculation = CalcState
Application.EnableEvents = EventState
Application.ScreenUpdating = True
End Sub
Sub collectdata()
Dim MyFSO As New FileSystemObject 'declaring and initializing FSO (file system object)
Dim wkbsource As Workbook 'workbook variable to store the raw excel file
Dim iRow As Long ' to store the last blank row available in collected data sheet before pasting the raw data
Dim iTotalRow As Long 'to store the last non-blank row availbe in collected data sheet after pasting the raw data
Dim sPath As String ' to store the selected folder path
Dim SourceFolder As Folder 'to store folder path
Dim MyFile As File 'File variable for FSO
Dim Filename As String ' to store the excel file name for column A in collected data sheet
Dim iTotalFiles As Long 'to store the count of all excel files available in selected folder
Dim DialogBox As FileDialog 'file dialog to select the folder name
Dim Hours As Range 'To store the Hours of test ran
Dim IRtestdate As Range 'To store the date of the IR reading
Dim iFailsRow As Long 'to identify the last cell used, therefore identifies how many pcs ran
Dim Count As Long 'To store the amount of bad pcs
Dim AOI As Range 'To determine the area for which the program will look for bad pcs
'Begin Optimizer
Call OptimizeCode_Begin
Set DialogBox = Application.FileDialog(msoFileDialogFolderPicker) 'Assiging Folder picker dialog box
With DialogBox
.Title = "Select Folder"
.AllowMultiSelect = False
.InitialFileName = Application.DefaultFilePath
If .Show <> -1 Then Exit Sub 'no folder selected
sPath = .SelectedItems(1)
End With
'Check wheather selected folder exists or not
If Not MyFSO.FolderExists(sPath) Then
MsgBox "folder is not available.", vbOKOnly + vbCritical, "Error"
appication.ScreenUpdating = True
Exit Sub
End If
Set SourceFolder = MyFSO.GetFolder(sPath)
'Get the count of all excel files available in Sourcefolder (selected folder)
iTotalFiles = 0
For Each MyFile In SourceFolder.Files
If MyFSO.GetExtensionName(MyFile) = "xls" Then
iTotalFiles = iTotalFiles + 1
End If
Next MyFile
'Code to terminate the code if there is no Excel file availbe in Selected folder
If iTotalFiles = 0 Then
MsgBox "no Excelfile available.", vbOKOnly + vbCritical, "Error"
Application.ScreenUpdating = True
Exit Sub
End If
'Code to compile the data from Raw file to collecter
For Each MyFile In SourceFolder.Files
If MyFSO.GetExtensionName(MyFile) = "xls" Then
'Code to identify the last Blank row number in collated data sheet
iRow = ThisWorkbook.Sheets("Collated data").Range("B" & Rows.Count).End(xlUp).Row + 1
'Store the file name
Filename = MyFSO.GetFileName(MyFile)
'Code to open the raw file
Set wkbsource = Workbooks.Open(Filename:=MyFile, ReadOnly:=True)
On Error GoTo Nextloop
'finds out if the file belongs to Jeff, if not it skips the file
Set requester = wkbsource.ActiveSheet.Range("F3")
If requester.Value = "Jeff Horn" Then
GoTo Valid
Else
GoTo Nextloop
End If
Valid:
'code to identify If the Sheet is Blank
If IsEmpty(wkbsource.Sheets("Uncorrected").Range("N23").Value) = True Then GoTo Nextloop
'finds Group number and pastes to collated sheet
ThisWorkbook.Sheets("Collated Data").Range("B" & iRow) = wkbsource.Sheets("Uncorrected").Range("F2")
'finds Test ran and pastes to collated sheet
ThisWorkbook.Sheets("Collated Data").Range("C" & iRow) = wkbsource.Sheets("Uncorrected").Range("B1")
'Code to read the hours ran, find the highest value, and display it in the Collated data sheet
With wkbsource.Sheets("Uncorrected").Range("M21:T21")
Set Hours = .Find("*", LookIn:=xlValues, SearchDirection:=xlPrevious)
End With
If Hours Is Nothing Then
GoTo Testdate
Else: On Error GoTo Testdate
End If
ThisWorkbook.Sheets("Collated Data").Range("D" & iRow) = Hours
Testdate:
'Finds the the IRtestdate and Pastes that value into the Collated data sheet
With wkbsource.Sheets("Uncorrected").Range("M18:T18")
Set IRtestdate = .Find("*", LookIn:=xlValues, SearchDirection:=xlPrevious)
End With
If IRtestdate Is Nothing Then
GoTo FailsCounter
Else: On Error GoTo FailsCounter
End If
ThisWorkbook.Sheets("Collated Data").Range("E" & iRow) = IRtestdate
FailsCounter:
'code to identify the column that needs to be counted dynamically based on Hours tested
iFailsRow = wkbsource.Sheets("Uncorrected").Range("N" & Rows.Count).End(xlUp).Row
Y = Hours.Row + 2
X = Hours.Column
Count = 0
Set AOI = wkbsource.Sheets("Uncorrected").Range(Cells(Y, X), Cells(iFailsRow, X))
'Code to count the number of fails in each file
For Each cell In AOI
If cell.Value <= 0.001 Then
Count = Count + 1
On Error Resume Next
End If
Next
Total_Parts = iFailsRow - 22
ThisWorkbook.Sheets("Collated Data").Range("F" & iRow) = Count & " of " & Total_Parts
'Code to identify the last non-blank row in collated data sheet after pasting raw data
iTotalRow = ThisWorkbook.Sheets("Collated data").Range("B" & Rows.Count).End(xlUp).Row
'Code to update the file name
ThisWorkbook.Sheets("Collated Data").Range("A" & iRow & ":A" & iTotalRow).Value = Filename
Application.CutCopyMode = False
Nextloop:
wkbsource.Close savechanges:=False
On Error GoTo ErrorHandle
ErrorHandle:
Set wkbsource = Nothing
End If
Next MyFile
MsgBox "Data collated", vbOKOnly + vbInformation, "Done!"
'End optimizer
Call OptimizeCode_End
End Sub

Stuck trying to delete a row in a table

I'm stuck trying to delete a row in a table after copying and pasting it to another Workbook table. It worked when I was activating workbooks and sheets but now has stopped. Can this be done the way I have the code now? Any help is greatly appreciated. I have been trying to fix this for two days now..
Sub MoveOrdertoDelivery()
'
' Move_Order Macro
' Move the next line on the Delivery Log for the day
'
'*************************************************************************
' New Code for MoveLinetoDeliverySchedule
'*************************************************************************
Dim copyrng As Range
Dim rngOld As Range
Dim msgRes As VbMsgBoxResult
Dim checkcellrange As Range
Dim strFileDir As String
' new to use the better code****DELETE AFTER IT WORKS
Dim destinationSheetName As String
Dim originatingWorkbookName As String
Dim originatingWorkbook As Workbook
Dim destinationWorkbook As Workbook
Dim originatingSheetName As String
Dim destinationFileName As String
Dim originatingTableName As String
Dim destinationTableName As String
strFileDir = ThisWorkbook.Path
originatingWorkbookName = ActiveWorkbook.Name
originatingSheetName = ActiveSheet.Name
destinationFileName = strFileDir & "\Door Delivery Schedule.xlsm"
Set checkcellrange = Range("A1:ZZ3")
Set rngOld = ActiveCell
Set originatingWorkbook = Workbooks.Open(strFileDir & "\" & originatingWorkbookName)
ActiveSheet.Cells(ActiveCell.Row, 1).Select
Set copyrng = Range(ActiveCell.Offset(0, 0), ActiveCell.Offset(0, 11))
copyrng.Select
If Intersect(checkcellrange, copyrng) Is Nothing Then
originatingTableName = ActiveCell.ListObject.Name
Else
msgRes = MsgBox("Please select a table Row!", vbOKCancel)
Exit Sub
End If
If Intersect(checkcellrange, rngOld) Is Nothing Then
If Not allBlank(ThisWorkbook.Sheets(originatingSheetName).Range(ActiveCell.Offset(0, 0), ActiveCell.Offset(0, 11))) Then
msgRes = MsgBox("Proceed?", vbOKCancel, "You are about to Move this row to the Door Delivery Schedule.")
If msgRes = vbOK Then
Selection.Copy
On Error Resume Next
tablerow = Selection.Row - Selection.ListObject.Range.Row
If Err.Number = 91 Then
MsgBox "Please select a line with data in it!"
Exit Sub
End If
'originatingWorkbook.Sheets(originatingSheetName).Range(ListObjects(originatingTableName).ListRows(tablerow)).Copy
'originatingWorkbook.Sheets(originatingSheetName).Range(copyrng).Copy
Set destinationWorkbook = Workbooks.Open(destinationFileName)
destinationWorkbook.Sheets("Orders For Delivery").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).PasteSpecial
destinationWorkbook.Save
destinationWorkbook.Close
'originatingWorkbook.Sheets(originatingSheetName).Selection.Delete Shift:=
Windows("Production Schedule Main.xlsm").Activate
Sheets("Completed Orders").Activate
copyrng.Select
originatingWorkbook.Sheets(originatingSheetName).Selection.ListObject.ListRows(tablerow).Delete
Selection.Delete Shift:=x1Up
Exit Sub
Else
End If
Else
MsgBox ("Please select a row with data in it.")
End If
Else
MsgBox ("Please select a legal row in the field.")
End If
End Sub

Copy selection of Word tables to Excel

I have modified the code from Macro to export MS Word tables to Excel sheets to copy a sequential interval of tables (e.g. 1 to 4) instead of copying all tables, as in the original code.
But I cannot figure out how to copy a selection of tables in the Word document (e.g. tables 1, 3, 7, 8).
Help to tweak the relevant section of code much appreciated!
'For tableStart = 1 To tableTo '<- copies all tables
For tableStart = 1 To 4 '<- copies sequential interval of tables
With .tables(tableStart)
.Range.Copy
Target.Activate
Target.Parent.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
'ActiveSheet.Paste <- pastes with formatting
Set Target = Target.Offset(.Rows.Count + 2, 0)
End With
Next tableStart
The entire macro is:
Sub ImportWordTables()
Dim WordApp As Object
Dim WordDoc As Object
Dim arrFileList As Variant, FileName As Variant
Dim tableNo As Integer 'table number in Word
Dim tableStart As Integer
Dim tableTot As Integer
Dim Target As Range
On Error Resume Next
arrFileList = Application.GetOpenFilename("Word files (*.doc; *.docx),*.doc;*.docx", 2, _
"Browse for file containing table to be imported", , True)
If Not IsArray(arrFileList) Then Exit Sub '<-user cancelled import file browser
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = False
Range("A:AZ").ClearContents
Set Target = Worksheets("MySheet").Range("A1")
For Each FileName In arrFileList
Set WordDoc = WordApp.Documents.Open(FileName, ReadOnly:=True)
With WordDoc
tableNo = WordDoc.tables.Count
tableTot = WordDoc.tables.Count
If tableNo = 0 Then
MsgBox WordDoc.Name & "Contains no tables", vbExclamation, "Import Word Table"
End If
'For tableStart = 1 To tableTo '<- copies all tables
For tableStart = 1 To 4 '<- copies interval of tables
With .tables(tableStart)
.Range.Copy
Target.Activate
Target.Parent.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
'ActiveSheet.Paste <- pastes with formatting
Set Target = Target.Offset(.Rows.Count + 2, 0)
End With
Next tableStart
.Close False
End With
Next FileName
WordApp.Quit
Set WordDoc = Nothing
Set WordApp = Nothing
End Sub
You could supply the list of tables as an array. I added this to the restructured code below. You provide a variant containing an array of the table numbers you want to copy to the ImportWordTables sub. I'll leave it to you to modify the code by making the parameter optional so that you either copy all tables or the tables in the list.
Option Explicit
Public Enum TableImportError
NoTables
UnexpectedIndex
End Enum
Public Sub ImportWordTables(ByVal ipTableList As Variant)
Dim arrFileList As Variant
If Not TryGetFileNameList(arrFileList) Then Exit Sub
Dim WordApp As Object
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = False
' the range should be qualified by the relevant wb/ws
Range("A:AZ").ClearContents
Dim FileName As Variant
For Each FileName In arrFileList
Dim WordDoc As Object
Set WordDoc = WordApp.Documents.Open(FileName, ReadOnly:=True)
Dim myReason As TableImportError
If Not TryImportTables(WordDoc, ipTableList, myReason) Then
Select Case myReason
Case TableImportError.NoTables
MsgBox WordDoc.Name & "Contains no tables", vbExclamation, "Import Word Table"
Case TableImportError.UnexpectedIndex
MsgBox WordDoc.Name & "Unexpected index", vbExclamation, "The table indexes exceed the total table count. No tables copies"
End Select
End If
Next FileName
WordApp.Quit
Set WordDoc = Nothing
Set WordApp = Nothing
End Sub
Private Function TryGetFileNameList(ByRef opFileList As Variant) As Boolean
On Error Resume Next
opFileList = Application.GetOpenFilename("Word files (*.doc; *.docx),*.doc;*.docx", 2, _
"Browse for file containing table to be imported", , True)
On Error GoTo 0
TryGetFileNameList = IsArray(opFileList)
End Function
Private Function TryImportTables(ByRef ipDoc As Word.Document, ByVal ipTableList As Variant, ByRef opReason As TableImportError) As Boolean
TryImportTables = False
If ipDoc.Tables.Count = 0 Then
opReason = TableImportError.NoTables
Exit Function
End If
Dim myTable As Variant
For Each myTable In ipTableList
If myTable > ipDoc.Tables.Count Then
opReason = TableImportError.UnexpectedIndex
Exit Function
End If
Next
For Each myTable In ipTableList
With ipDoc.Tables.Item(myTable)
.Range.Copy
' replaced Target by worksheet refernce
' ideally this item should be passed as a parameter
' or second best defined as a module level variable.
'
' worksheets should be qualified by the relevant wb
With Worksheets("MySheet")
.Activate
.Parent.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
Set .Range("A1") = .Range("A1").Offset(.Rows.Count + 2, 0)
'.Paste
End With
End With
Next
TryImportTables = True
End Function
The code above compiles and doesn't give any unexpected Code Inspection warning by the free and fantastic Rubberduck addin for VBA. However as its a radical restructuring I can't guarantee it will work exactly as you previous code so please do check if you decide to use it.
Set this code of yours as a Function that you call from the Main Sub with the a TableIndex
With .tables(tableIndex)
.Range.Copy
Target.Activate
Target.Parent.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
'ActiveSheet.Paste <- pastes with formatting
Set Target = Target.Offset(.Rows.Count + 2, 0)
End With
Set the tableIndex variable as a Random number assigned from the collection of tables in the Word document. You'll have to decide how many times you will iterate thru the code so you get the random sample size you want but the basic code might look like this:
Sub ReturnRandomNumber()
Dim TableIndex As Integer
Dim WordDoc As Word.Document
Randomize
TableIndex = Int((WordDoc.Tables.Count * Rnd) + 1)
CopyRandomTable WordDoc, TableIndex
End Sub
Function CopyRandomTable(ByRef WordDoc As Word.Document, ByRef TableIndex As Integer)
With WordDoc.Tables(TableIndex)
.Range.Copy
Target.Activate
Target.Parent.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
'ActiveSheet.Paste <- pastes with formatting
Set Target = Target.Offset(.rows.Count + 2, 0)
End With
End Function
The RND function might also return the same table index so you should think about how you might want to deal with that ... like possibly setting up an array of table indexes already used and then act accordingly.
For more information about Randomize and the RND function here is a Microsoft Article on the RND Function.
Solved, but input box and memory capacity issue still to be fixed.
Replace With .tables(tableIndex) section in my first example above with the following:
'For array
Dim tables() As Variant
Dim tableCounter As Long
tables = Array(1, 3, 7) '<- define array manually here
For tableCounter = LBound(tables) To UBound(tables)
With .tables(tables(tableCounter))
.Range.Copy
Target.Activate
'Target.Parent.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False '<- gives RAM capacity problems!
ActiveSheet.Paste '<- pastes with formatting
Set Target = Target.Offset(.Rows.Count + 2, 0)
End With
Next tableCounter

Excel macro infinite loop keeps asking for user's input and can't "step into" to debug

I am creating a few macros to do the following in Excel 2010:
1. Upon creating a new worksheet ask for what the user wants to name his/her worksheet and sets the new worksheet to the name provided; calls Sort_Active_Book and Rebuild_TOC in order
2. Sort_Active_Book: Asks the user if he/she wants to sort the workbook's worksheets in ascending/descending order and proceeds to do so.
3. Rebuild_TOC: Deletes the Table of Contents page and rebuilds it based on all the worksheets in the workbook minus the TOC itself.
My problem is Excel keeps asking me to input the name of the new worksheet to be created and does not progress any further in the code. I notice it manages to create the named worksheet and asks me if I would like to sort ascending or descending but then proceeds to ask me again the name of the new worksheet. Could anyone please point out how to fix this and provide a code fix (if possible) please?
What I have already
This code portion is from ThisWorkbook, this is what prompts the user for the name of the worksheet upon creation.
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Dim sName As String
Dim bValidName As Boolean
Dim i As Long
bValidName = False
Do While bValidName = False
sName = InputBox("Please name this new worksheet:", "New Sheet Name", Sh.Name)
If Len(sName) > 0 Then
For i = 1 To 7
sName = Replace(sName, Mid(":\/?*[]", i, 1), " ")
Next i
sName = Trim(Left(WorksheetFunction.Trim(sName), 31))
If Not Evaluate("ISREF('" & sName & "'!A1)") Then bValidName = True
End If
Loop
Sh.Name = sName
Call Sort_Active_Book
Call Rebuild_TOC
End Sub
These two macros are in "Module 1":
Sub Sort_Active_Book()
Dim TotalSheets As Integer
Dim p As Integer
Dim iAnswer As VbMsgBoxResult
' Move the TOC to the begining of the document.
Sheets("TOC").Move Before:=Sheets(1)
' Prompt the user as to which direction they wish to
' sort the worksheets.
iAnswer = MsgBox("Sort Sheets in Ascending Order?" & Chr(10) & "Clicking No will sort in Descending Order", vbYesNoCancel + vbQuestion + vbDefaultButton1, "Sort Worksheets")
For TotalSheets = 1 To Sheets.Count
For p = 2 To Sheets.Count - 1
' If the answer is Yes, then sort in ascending order.
If iAnswer = vbYes Then
If UCase$(Sheets(p).Name) = "TOC" Then
Sheets(p).Move Before:=Sheets(1)
ElseIf UCase$(Sheets(p).Name) > UCase$(Sheets(p + 1).Name) Then
Sheets(p).Move After:=Sheets(p + 1)
End If
' If the answer is No, then sort in descending order.
ElseIf iAnswer = vbNo Then
If UCase$(Sheets(p).Name) = "TOC" Then
Sheets(p).Move Before:=Sheets(1)
ElseIf UCase$(Sheets(p).Name) < UCase$(Sheets(p + 1).Name) Then
Sheets(p).Move After:=Sheets(p + 1)
End If
End If
Next p
Next TotalSheets
End Sub
and
Sub Rebuild_TOC()
Dim wbBook As Workbook
Dim wsActive As Worksheet
Dim wsSheet As Worksheet
Dim lnRow As Long
Dim lnPages As Long
Dim lnCount As Long
Set wbBook = ActiveWorkbook
With Application
.DisplayAlerts = False
.ScreenUpdating = False
End With
' If the TOC sheet already exist delete it and add a new worksheet.
On Error Resume Next
With wbBook
.Worksheets(“TOC”).Delete
.Worksheets.Add Before:=.Worksheets(1)
End With
On Error GoTo 0
Set wsActive = wbBook.ActiveSheet
With wsActive
.Name = “TOC”
With .Range(“A1:B1”)
.Value = VBA.Array(“Table of Contents”, “Sheet # – # of Pages”)
.Font.Bold = True
End With
End With
lnRow = 2
lnCount = 1
' Iterate through the worksheets in the workbook and create sheetnames, add hyperlink
' and count & write the running number of pages to be printed for each sheet on the TOC.
For Each wsSheet In wbBook.Worksheets
If wsSheet.Name <> wsActive.Name Then
wsSheet.Activate
With wsActive
.Hyperlinks.Add .Cells(lnRow, 1), “”, _
SubAddress:=”‘” & wsSheet.Name & “‘!A1”, _
TextToDisplay:=wsSheet.Name
lnPages = wsSheet.PageSetup.Pages().Count
.Cells(lnRow, 2).Value = “‘” & lnCount & “-” & lnPages
End With
lnRow = lnRow + 1
lnCount = lnCount + 1
End If
Next wsSheet
wsActive.Activate
wsActive.Columns(“A:B”).EntireColumn.AutoFit
With Application
.DisplayAlerts = True
.ScreenUpdating = True
End With
End Sub
You are creating a new sheet with sub Rebuild_TOC. Causing the newsheet macro to run again.
You will need to avoid running the newsheet macro by adding a enableevents = false and true surrounding your code when creating a new sheet for your TOC. The rest of your code appears to be working as you want it to.
Application.EnableEvents = False
With wbBook
.Worksheets("TOC").Delete
.Worksheets.Add Before:=.Worksheets(1)
End With
Application.EnableEvents = True
Why would you want to delete the TOC worksheet, why not just update it?

Need some advice on how to stream line ACCESS/EXCEL VBA

I wrote this Access/VBA program. It works but only when I am not running other applications or few users are in the database. I need some ideas on streamlining the code. So it is not so system intensive. The program basically allows a user to pick a folder and then combines all worksheets in that folder in one excel document. My current idea is just to tell users to close all excel files when trying to run the program. Please Help:
Sub Excel_open()
Dim myXL As Excel.Application
Dim myXLS As Excel.Workbook
Const errExcelNotRunning = 429
On Error GoTo HandleIt
Set myXL = GetObject(, "Excel.application")
myXL.Visible = True
Set myXLS = myXL.Workbooks.Add
Call CombineWorkbooks(myXL)
HandleIt:
If Err.Number = errExcelNotRunning Then
Set myXL = CreateObject("Excel.Application")
Err.Clear
Resume Next
End If
End Sub
Sub CombineWorkbooks(myXL)
'Macro that combines the files into one folder
myXL.AskToUpdateLinks = False
myXL.DisplayAlerts = False
Dim CurFile As String, dirloc As String, strNamesheet As String
Dim DestWB As Workbook
Dim ws As Object ' allows for diffrent sheet types
'Add select the director function
dirloc = GetFolderName & "\" 'location of files not working want to select the file only
CurFile = Dir(dirloc & "*.xls*")
myXL.ScreenUpdating = False
myXL.EnableEvents = False
Set DestWB = Workbooks.Add(xlWorksheet)
Do While CurFile <> vbNullString
Dim OrigWB As Workbook
Set OrigWB = Workbooks.Open(FileName:=dirloc & CurFile, ReadOnly:=True)
'need to change a name active name is not doing it
CurFile = Left(CurFile, 4) ' This is no longer 29
'CurFile = Left(Left(CurFile, Len(CurFile) - 5), 29)
For Each ws In OrigWB.Sheets
ws.Copy After:=DestWB.Sheets(DestWB.Sheets.Count)
' Use the name to give the sheet a name
strNamesheet = Left((ws.Name), 25) & ";"
If OrigWB.Sheets.Count > 1 Then
DestWB.Sheets(DestWB.Sheets.Count).Name = strNamesheet & CurFile ' & ws.Index
Else
DestWB.Sheets(DestWB.Sheets.Count).Name = CurFile
End If
Next
OrigWB.Close SaveChanges:=False
CurFile = Dir
Loop
myXL.DisplayAlerts = False
DestWB.Sheets(1).Delete
myXL.DisplayAlerts = True
myXL.ScreenUpdating = True
myXL.EnableEvents = True
Set DestWB = Nothing
Call Delete_empty_Sheets(myXL)
Call Sort_Active_Book
MsgBox "Done"
'Call Xcombine_the_Matching
End Sub
Sub Delete_empty_Sheets(myXL)
'goes through all sheets and deletes
Reset_the_search:
For Each wsElement In Worksheets
If wsElement.Range("A2") = "" And wsElement.Range("B2") = "" Then
myXL.DisplayAlerts = False
wsElement.Delete
GoTo Reset_the_search
myXL.DisplayAlerts = True
End If
Next wsElement
End Sub
Sub Xcombine_the_Matching()
'I think I can make the order work
'change and transpose the array
Dim varStart As Variant
Dim wsCompare As Worksheet
Dim strMatch As String
'Dim varCompare As Variant
Dim strVareince As String
Dim strCurrentName As String
'you need to build a loop to solve this problem
For Each wsCompare In Worksheets
strVareince = Add_Array(Application.Transpose(wsCompare.Range("A1:Z1")))
For Each wsNompare In Worksheets
If wsNompare.Name <> strCurrentName Then
If strVareince = Add_Array(Application.Transpose(wsNompare.Range("A1:Z1"))) Then
MsgBox ("Matched with worksheet " & wsNompare.Name)
End If
End If
Next
Next
End Sub
Function array_to_string(x) As String
For Z = 1 To 26
array_to_string = array_to_string & x(Z, 1) & ";"
Next Z
End Function
Function GetFolderName(Optional OpenAt As String) As String
'Allows you to select the folder director that you want to combine
Dim lCount As Long
GetFolderName = vbNullString
With Application.FileDialog(msoFileDialogFolderPicker)
.InitialFileName = OpenAt
.Show
For lCount = 1 To .SelectedItems.Count
GetFolderName = .SelectedItems(lCount)
Next lCount
End With
End Function
Function Add_Array(x) As String
'turns an excel document
For d = 1 To UBound(x)
Add_Array = Add_Array & x(d, 1)
Next d
End Function
Sub Read_data()
'this the
End Sub
Sub Sort_Active_Book()
Dim i As Integer
Dim j As Integer
Dim iAnswer As VbMsgBoxResult
'
' Prompt the user as which direction they wish to
' sort the worksheets.
'
iAnswer = MsgBox("Sort Sheets in Ascending Order?" & Chr(10) _
& "Clicking No will sort in Descending Order", _
vbYesNoCancel + vbQuestion + vbDefaultButton1, "Sort Worksheets")
For i = 1 To Sheets.Count
For j = 1 To Sheets.Count - 1
'
' If the answer is Yes, then sort in ascending order.
'
If iAnswer = vbYes Then
If UCase$(Sheets(j).Name) > UCase$(Sheets(j + 1).Name) Then
Sheets(j).Move After:=Sheets(j + 1)
End If
'
' If the answer is No, then sort in descending order.
'
ElseIf iAnswer = vbNo Then
If UCase$(Sheets(j).Name) < UCase$(Sheets(j + 1).Name) Then
Sheets(j).Move After:=Sheets(j + 1)
End If
End If
Next j
Next i
End Sub
You are passing your Excel Application object into your subroutines, but not using it fully, neither are you explicitly referencing the libraries:
Sub CombineWorkbooks(myXL)
Dim DestWB As Excel.Workbook ' <<<
Set DestWB = myXL.Workbooks.Add(xlWorksheet) ' <<<
End Sub
Run through your code and fix all of these first, then test & supply more feedback on what the precise symptoms of the problems are.

Resources