Just have a quick question regarding paste. I have a script that exports individual rows into a newly created workbook. However, the problem is that the pasted values are in the form of image. Additionally, the comments are skipped. I used the same code for pasting into other sheets of the same workbook, and there is no issue.
I can't seem to find the reason why. Any help would be greatly appreciated.
Thanks
Private Sub DC_1Month_Button_Click()
'Searches for crews working on MFDC (7343) and exports a new spreadsheet looking 3 weeks ahead for each person
If MsgBox("Export DC individual schedules?") = vbNo Then
Exit Sub
End If
On Error GoTo CleanFail
Dim nowCol As Integer, lastCol As Integer, endCol As Integer, crewRow As Integer
Dim masterSheet As Worksheet, newExcel As Object, newBook As Workbook, newSheet As Worksheet
Dim startRow As Integer, endRow As Integer
Dim currentName As String, currentProject As String
startRow = 3
endRow = UsedRange.Row - 1 + UsedRange.Rows.count
lastcoln = UsedRange.Column - 1 + UsedRange.Columns.count
Set masterSheet = ThisWorkbook.Worksheets("Master Schedule")
'Find columns for today and date 3 weeks after
nowCol = Range(Cells(2, 1), Cells(2, lastcoln)).Find(what:=Month(Date) & "/" & Day(Date) & "/" & Year(Date)).Column
endCol = Range(Cells(2, 1), Cells(2, lastcoln)).Find(what:=Month(DateAdd("d", 30, Date)) & "/" & Day(DateAdd("d", 30, Date)) & "/" & Year(DateAdd("d", 30, Date))).Column
'Disable screen flashing while doing copying and exports
Application.ScreenUpdating = False
'Loop through crew members
For i = 3 To endRow
'Store current row's values
currentName = Replace(ActiveSheet.Cells(i, 2).Value, "SA: ", "")
currentProject = ActiveSheet.Cells(i, 3).Value
'Search the value from the Project column for the MFDC project number
If InStr(1, currentProject, "7343") > 0 Then
'Load schedule template
Set newExcel = CreateObject("Excel.Application")
newExcel.DisplayAlerts = False
newExcel.Workbooks.Open "\\VALGEOFS01\SurveyProjectManagers\304Schedule\Templates\DC_3Week_Template.xlsx"
Set newBook = newExcel.Workbooks(1)
Set newSheet = newBook.Worksheets(1)
'Copy and paste header rows
masterSheet.Range(masterSheet.Cells(1, nowCol), masterSheet.Cells(2, endCol)).Copy 'Destination:=newSheet.Range("A1")
Application.Wait (Now + TimeValue("0:00:01"))
newSheet.Range(newSheet.Cells(1, 6), newSheet.Cells(1, endCol - 1)).PasteSpecial xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
'Copy and paste crew member's location
masterSheet.Range(masterSheet.Cells(i, 2), masterSheet.Cells(i, 6)).Copy 'Destination:=newSheet.Range("A3")
Application.Wait (Now + TimeValue("0:00:01"))
newSheet.Range("A3").PasteSpecial Paste:=xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
'Copy schedule data for crew member
masterSheet.Range(masterSheet.Cells(i, nowCol), masterSheet.Cells(i, endCol)).Copy
Application.Wait (Now + TimeValue("0:00:01"))
newSheet.Cells(3, 6).PasteSpecial Paste:=xlPasteAll
Application.CutCopyMode = False
'Save individual's schedule
With newBook
.Title = currentName & " MFDC Schedule"
.SaveAs Filename:="\\VALGEOFS01\SurveyProjectManagers\304Schedule\MFDC Individual Schedules\" & currentName & " MFDC Schedule " & Format(Date, "yymmdd") & ".xlsx", AccessMode:=xlExclusive, ConflictResolution:=Excel.XlSaveConflictResolution.xlLocalSessionChanges
.Close (True)
End With
End If
Next i
CleanExit:
MsgBox "Export complete"
'Restore normal screen updating
Application.ScreenUpdating = True
Exit Sub
CleanFail:
If Err.Number <> 0 Then
Msg = "Error # " & Str(Err.Number) & " was generated by " & Err.Source & Chr(13) & Err.Description
MsgBox Msg, , "Error", Err.HelpFile, Err.HelpContext
End If
Resume CleanExit
Resume
End Sub
Related
I have attached the image of my mapping table and written these two functions referring to the mapping table that I created : (Table name is "Automation")
1)
Function GetRow(rowName As String) As String
Dim refRange As Range: Set refRange = Sheet14.Range("Automation")
On Error GoTo errProc
GetRow = WorksheetFunction.VLookup(rowName, refRange, 2, 0)
Exit Function
errProc:
If Err.Number = 1004 Then
Err.Raise "5000", "Something bad happened", "Value " & rowName & " not
found!!"
Else
Err.Raise Err.Number, Err.Source, Err.Description
End If
End Function
2)
Function GetMap(rowName As String) As String
Dim refRange As Range: Set refRange = Sheet14.Range("Automation")
On Error GoTo errProc
GetMap = WorksheetFunction.VLookup(rowName, refRange, 1, 0)
Exit Function
errProc:
If Err.Number = 1004 Then
Err.Raise "5000", "Something bad happened", "Value " & rowName & " not
found!!"
Else
Err.Raise Err.Number, Err.Source, Err.Description
End If
End Function
And this is the entire updated code :
Sub CopyRange(fromRange As Range, toRange As Range, completed As Double)
fromRange.Copy
toRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone,
SkipBlanks:=False, Transpose:=False
Application.StatusBar = "Copying In progress..." & Round(completed, 0) & "%
completed"
DoEvents
End Sub
Sub Header()
DestName = "Data Cost Estimate" 'Name of destination sheet
SourceName = "EST Actuals" 'Name of Source sheet
MyDir = "\Path\" 'Default directory path"
Const steps = 22 'Number of rows copied
ref = 13 'row in Estimate sheet in which 'Grand Total' is
present
Set Wb = ThisWorkbook
' disable certain excel features to speed up the process
Application.DisplayAlerts = False
'Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Application.Calculation = xlCalculationManual
MyFile = Dir(MyDir & "Estimate.xlsm") 'change file extension
ChDir MyDir
Set wkb = Workbooks.Open(MyDir + MyFile, UpdateLinks:=0)
Dim lnCol As Long
Dim last As Long 'Find the last non-blank cell in row 1
lnCol = wkb.Sheets(SourceName).Cells(ref,
Columns.Count).End(xlToLeft).Column
last = lnCol - 1
MsgBox "Last but one column is: " & last
Dim from, dest As String
from = GetRow(GetMap(wkb.Sheets(SourceName)))
j = Wb.Sheets(DestName).Cells(1, 1).EntireColumn.Find(What:=from,
LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows,
SearchDirection:=xlNext, MatchCase:=False).Row
Call CopyRange(Sheets(SourceName).Range("C18:R18"),
Wb.Sheets(DestName).Cells(j, 2), completed)
completed = completed + (100 / steps)
Call CopyRange(Sheets(SourceName).Range("C20:R20"),
Wb.Sheets(DestName).Cells(j, 2), completed)
completed = completed + (100 / steps)
Call CopyRange(Sheets(SourceName).Range("C27:R27"),
Wb.Sheets(DestName).Cells(j, 2), completed)
completed = completed + (100 / steps)
wkb.Close
MyFile = Dir()
'Application.ScreenUpdating = True
Application.DisplayAlerts = True
'Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
ActiveSheet.DisplayPageBreaks = True
When I try the code, I get an error saying "Object doesn't support this property" for the part where I want the function to return a value. I can't figure out how to correct this. CopyRange is another sub that I use for my Progress bar.
So after a lot of trial and error and help from #tomjohnriddle, here is the function and the correct code:
1) Function:
Function GetSourceKey(destinationKey As String) As String
Dim refRange As Range: Set refRange = Sheet14.Range("Mapping table name")
On Error GoTo errProc
GetSourceKey = WorksheetFunction.VLookup(destinationKey,
ThisWorkbook.Sheets("Sheet name in which mapping table is present").[Mapping table name], 2, 0)
Exit Function
errProc:
If Err.Number = 1004 Then
Err.Raise "5000", "Something bad happened", "Value " & destinationKey & "
not found!!"
Else
Err.Raise Err.Number, Err.Source, Err.Description
End If
End Function
2) Code :
Option Explicit
Sub CopyRange(fromRange As Range, toRange As Range, completed As Double)
fromRange.Copy
toRange.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone,
SkipBlanks:=False, Transpose:=False
Application.StatusBar = "Copying In progress..." & Round(completed, 0) & "%
completed"
DoEvents
End Sub
Sub Header()
DestName = "x" 'Name of destination sheet
SourceName = "y" 'Name of Source sheet
MyDir = "\Path\" 'Default directory path"
Const steps = 22 'Number of rows copied
ref = 13 'row in Estimate sheet in which 'Grand Total' is present
Set DestWb = ThisWorkbook 'Setting Destination workbook
Dim DestSheet As Worksheet
Dim SrcSheet As Worksheet
' disable certain excel features to speed up the process
Application.DisplayAlerts = False
'Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
MyFile = Dir(MyDir & "Estimate*.xls*") 'change file extension
ChDir MyDir
Set SrcWb = Workbooks.Open(MyDir + MyFile, UpdateLinks:=0)
completed = 0
Application.StatusBar = "Copying In progress..." & Round(completed,
0) & "% completed"
'Find the last non-blank cell in row ref
lnCol = SrcWb.Sheets(SourceName).Cells(ref,
Columns.Count).End(xlToLeft).Column
last = lnCol - 1 'To get penultimate column
Set DestSheet = DestWb.Sheets(DestName)
Set SrcSheet = SrcWb.Sheets(SourceName)
destTotalRows = DestSheet.Cells(Rows.Count, 1).End(xlUp).Row 'Finding last non-blank cell in Column 1 in Destination sheet
MsgBox "Last row is: " & destTotalRows
For i = 1 To destTotalRows
destKey = DestSheet.Cells(i, 1)
If destKey = "" Then GoTo endFor
sourceKey = GetSourceKey(destKey)
If sourceKey = "" Then GoTo endFor
Debug.Print "DestKey", destKey, "SourceKey", sourceKey
k = DestSheet.Cells(1, 1).EntireColumn.Find(What:=destKey, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False).Row
j = SrcSheet.Cells(1, 2).EntireColumn.Find(What:=sourceKey, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False).Row
Debug.Print j, k
Call CopyRange(SrcSheet.Range(Cells(j, 3), Cells(j, 3).End(xlToRight)), DestSheet.Cells(k, 2), completed)
completed = completed + (100 / steps)
endFor:
Next i
SrcWb.Close
Application.StatusBar = "Copying is complete"
DoEvents
MyFile = Dir()
Dim x As Long
'Find the last non-blank cell in row 1
DestColCount = Cells(1, Columns.Count).End(xlToLeft).Column
DestWb.Sheets(DestName).Columns(2).Copy
For x = 3 To DestColCount
Columns(x).PasteSpecial Paste:=xlPasteFormats
Next
'Application.ScreenUpdating = True
Application.DisplayAlerts = True
'Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
ActiveSheet.DisplayPageBreaks = True
End Sub
Below is a macro which I wrote about three years ago, when I was a much less proficient VBA coder than I am today. There are a number of obvious things which I would simplify/do differently today. However, it is still in use and generally works. The manager of the relevant admin process copies and pastes this code into different workbooks every time we set up a new customer, and changes around a few of the variables. This has worked fine until today, when it has suddenly started generating a "subscript out of range" error when used in a new worksheet.
The error generates on the line workdaybook.Sheets("Sheet1").Copy salesBook.Worksheets(Sheets.Count). I have checked and "salesBook" has been defined. However, hovering the cursor over "salesBook.worksheets(Sheets.Count) brings up a "subscript out of range" message.
I know that overall, this code isn't the best-written in the world, but I am puzzled by it suddenly failing on this line, having worked previously for about three years when pasted into multiple different workbooks.
Option Explicit
Sub salescalc()
'Application.DisplayAlerts = False
'On Error GoTo Errorcatch
Application.ScreenUpdating = True
Dim salesBook As Workbook
Dim CurrentWeekSheet As Worksheet
Set salesBook = ThisWorkbook
Set CurrentWeekSheet = ThisWorkbook.ActiveSheet
Dim workday As Date
Dim nextworkday As Date
Dim workdaybook As Workbook
workday = InputBox("Insert date in format dd/mm/yy", "userdate")
nextworkday = workday + 1
'find bottom row of "table"
Dim bottomrow As Range
For x = 1 To 6500
If CurrentWeekSheet.Cells(x, 1).Interior.ColorIndex = 19 Then
coloured_row = Range("A" & x).Row
End If
Next x
Set bottomrow = Range("A" & coloured_row)
'finds beginning and end of day's range
Dim workdayrange As Range
Dim nRow As Long
Dim workday_date As Variant
Dim nStart As Long, nEnd As Long
' Work out where the range should start - works
For nRow = 1 To 65536 'change this to xldown
If CurrentWeekSheet.Range("A" & nRow).Value = workday Then
'nStart = nRow + 3
nStart = nRow + 1
Exit For
End If
Next nRow
' Work out where the range should end - works
For nRow = nStart To 65536
If CurrentWeekSheet.Range("A" & nRow).Value = nextworkday Or Range("A" & nRow).Row = bottomrow.Row Then
nEnd = nRow
Exit For
End If
Next nRow
'distinction between bottom row and next date - offset less for bottomrow
If nEnd = bottomrow.Row Then
nEnd = nEnd
Else
If nEnd <> bottomrow.Row Then
nEnd = nEnd - 2
End If
End If
Set workdayrange = CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd)
workday_date = Format(workday, "dd-mm-yy")
'identify which month is referred to based on date input by user (workday_date)- this will need to be updated in 2016
If Year(workday_date) <> 2017 Then
MsgBox "Date must be in 2017. If you require info for another year, please contact xxxxx."
Exit Sub
Else
Workbooks.Open ("U:\\(Folder)\\(subfolder)\\(Subfolder)\\2017\\" & workday_date & ".xlsx")
Worksheets("Sheet1").Activate
End If
Set workdaybook = ActiveWorkbook
Dim workdaysheet As Worksheet
Set workdaysheet = ActiveSheet
workdaybook.Activate
workdaybook.Sheets("Sheet1").Copy salesBook.Worksheets(Sheets.Count)
ActiveSheet.Name = "salesdata"
Dim sheetforcopy As Worksheet
Set sheetforcopy = Sheets.Add
sheetforcopy.Name = "Sheetforcopy"
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).EntireRow.Copy
sheetforcopy.Range("A185").PasteSpecial xlPasteValues
sheetforcopy.Range("F" & nStart & ":F" & nEnd).Formula = "=if(D185<>0,SUMIFS(salesdata!E:E,salesdata!B:B,""*""&D185&""*"",salesdata!B:B,""*Total*""),"""")"
sheetforcopy.Range("L" & nStart & ":L" & nEnd).Formula = "=IF(IF(ISNUMBER(B185)=TRUE,COUNTIF(salesdata!B:B,""*""&B185&""*"")-1,"""")=-1,0,IF(ISNUMBER(B185)=TRUE,COUNTIF(salesdata!B:B,""*""&B185&""*"")-1,""""))"
sheetforcopy.Range("F" & nStart & ":F" & nEnd).Copy
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).PasteSpecial xlPasteValues
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).Copy
CurrentWeekSheet.Range("F" & nStart & ":F" & nEnd).PasteSpecial xlPasteValues
sheetforcopy.Range("L" & nStart & ":L" & nEnd).Copy
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).PasteSpecial xlPasteValues
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).Copy
CurrentWeekSheet.Range("L" & nStart & ":L" & nEnd).PasteSpecial xlPasteValues
Application.DisplayAlerts = False
Worksheets("salesdata").Delete
Worksheets("sheetforcopy").Delete
Application.DisplayAlerts = True
workdaybook.Close
CurrentWeekSheet.Activate
CurrentWeekSheet.Range("F" & nStart).Select
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
'Errorcatch: MsgBox "Error - (confidential message deleted)."
End Sub
following my post If cell value matches a UserForm ComboBox column, then copy to sheet.
I have managed to get the code to work to move the check the names and move then to the correct sheets.
The problem i am having is checking if the sheets exists. If it finds a match in the sheet and column 2 in the combobox but there is no sheet for the value then it crashes the code.
Once all the information has been copied to the relevant sheets, i would like it to display a msgbox telling the user how many rows of data have been copied to the respective sheets.
Dim i As Long, j As Long, lastG As Long, strWS As String, rngCPY As Range
With Application
.ScreenUpdating = False
.EnableEvents = False
.CutCopyMode = False
End With
On Error GoTo bm_Close_Out
' find last row
lastG = sheets("Global").Cells(Rows.Count, "Q").End(xlUp).row
For i = 3 To lastG
lookupVal = sheets("Global").Cells(i, "Q") ' value to find
' loop over values in "details"
For j = 0 To Me.ComboBox2.ListCount - 1
currVal = Me.ComboBox2.List(j, 2) ' value to match
If lookupVal = currVal Then
Set rngCPY = sheets("Global").Cells(i, "Q").EntireRow
strWS = Me.ComboBox2.List(j, 1)
On Error GoTo bm_Need_Worksheet '<~~ if the worksheet in the next line does not exist, go make one
With Worksheets(strWS)
rngCPY.Copy
.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Insert shift:=xlDown
End With
End If
Next j
Next i
GoTo bm_Close_Out
bm_Need_Worksheet:
On Error GoTo 0
With Worksheet
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsTemplate As Worksheet: Set wsTemplate = wb.sheets("Template")
Dim wsPayment As Worksheet: Set wsPayment = wb.sheets("Payment Form")
Dim wsNew As Worksheet
Dim lastRow2 As Long
Dim Contract As String: Contract = sheets("Payment Form").Range("C9").value
Dim SpacePos As Integer: SpacePos = InStr(Contract, "- ")
Dim Name As String: Name = Left(Contract, SpacePos)
Dim Name2 As String: Name2 = Right(Contract, Len(Contract) - Len(Name))
Dim NewName As String: NewName = strWS
Dim CCName As Variant: CCName = Me.ComboBox2.List(j, 0)
Dim lastRow As Long: lastRow = wsPayment.Range("U36:U53").End(xlDown).row
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
lastRow2 = wsPayment.Range("A23:A39").End(xlDown).row
Else
lastRow2 = wsPayment.Range("A18:A34").End(xlDown).row
End If
wsTemplate.Visible = True
wsTemplate.Copy before:=sheets("Details"): Set wsNew = ActiveSheet
wsTemplate.Visible = False
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
With wsPayment
For Each cell In .Range("A23:A39")
If Len(cell) = 0 Then
If sheets("Payment Form").Range("A20").value = "Network" Then
cell.value = NewName & " - " & Name2 & ": " & CCName
Else
cell.value = NewName & " - " & Name2 & ": " & CCName
End If
Exit For
End If
Next cell
End With
Else
With wsPayment
For Each cell In .Range("A18:A34")
If Len(cell) = 0 Then
If sheets("Payment Form").Range("A20").value = "Network" Then
cell.value = NewName & " - " & Name2 & ": " & CCName
Else
cell.value = NewName & " - " & Name2 & ": " & CCName
End If
Exit For
End If
Next cell
End With
End If
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
With wsNew
.Name = NewName
.Range("D4").value = wsPayment.Range("A23:A39").End(xlDown).value
.Range("D6").value = wsPayment.Range("L11").value
.Range("D8").value = wsPayment.Range("C9").value
.Range("D10").value = wsPayment.Range("C11").value
End With
Else
With wsNew
.Name = NewName
.Range("D4").value = wsPayment.Range("A18:A34").End(xlDown).value
.Range("D6").value = wsPayment.Range("L11").value
.Range("D8").value = wsPayment.Range("C9").value
.Range("D10").value = wsPayment.Range("C11").value
End With
End If
wsPayment.Activate
With wsPayment
.Range("J" & lastRow2 + 1).value = 0
.Range("L" & lastRow2 + 1).Formula = "=N" & lastRow2 + 1 & "-J" & lastRow2 + 1 & ""
.Range("N" & lastRow2 + 1).Formula = "='" & NewName & "'!L20"
.Range("U" & lastRow + 1).value = NewName & ": "
.Range("V" & lastRow + 1).Formula = "='" & NewName & "'!I21"
.Range("W" & lastRow + 1).Formula = "='" & NewName & "'!I23"
.Range("X" & lastRow + 1).Formula = "='" & NewName & "'!K21"
End With
End With
On Error GoTo bm_Close_Out
Resume
bm_Close_Out:
With Application
.ScreenUpdating = True
.EnableEvents = True
.CutCopyMode = True
End With
With help from Jeeped I have manage to get the code for copying the rows to the relevant sheets, and if the sheet doesn't exists then it create it. I just need help with problem two above.
Attempting to use a Worksheet Object that does not exist throws an error. If you catch that error and create a worksheet with the name that you are looking for, you can Resume back to the point where the error was thrown and continue your processing.
Private Sub CommandButton7_Click()
Dim i As Long, j As Long, lastG As Long, strWS As String, strMSG As String
dim rngHDR as range, rngCPY aS range
With Application
.ScreenUpdating = False
.EnableEvents = False
.CutCopyMode = False
End With
On Error GoTo bm_Close_Out
' find last row
lastG = Sheets("Global").Cells(Rows.Count, "Q").End(xlUp).Row
For i = 3 To lastG
lookupVal = Sheets("Global").Cells(i, "Q") ' value to find
' loop over values in "details"
For j = 0 To Me.ComboBox2.ListCount - 1
currVal = Me.ComboBox2.List(j, 2) ' value to match
If lookupVal = currVal Then
set rngHDR = Sheets("Global").Cells(1, "Q").EntireRow
set rngCPY = Sheets("Global").Cells(i, "Q").EntireRow
strWS = Me.ComboBox2.List(j, 1)
On Error GoTo bm_Need_Worksheet '<~~ if the worksheet in the next line does not exist, go make one
With WorkSheets(strWS)
rngCPY .copy
.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Insert shift:=xlDown
End With
exit for
End If
Next j
if j >= Me.ComboBox2.ListCount then _
strMSG = strMSG & "Not found: " & lookupVal & chr(10)
Next i
GoTo bm_Close_Out
bm_Need_Worksheet:
On Error GoTo 0
With Worksheets.Add(after:=Sheets(Sheets.Count))
.Name = strWS
'maybe make a header row here; watch out you do not lose your copy
rngHDR.copy destination:=.cells(1, 1)
End With
On Error GoTo bm_Close_Out
Resume
bm_Close_Out:
With Application
.ScreenUpdating = True
.EnableEvents = True
.CutCopyMode = False
End With
debug.print strMSG
'the next is NOT recommended as strMSG could possibly be VERY long
'if cbool(len(strMSG)) then msgbox strMSG
End Sub
There is a question about whether the new worksheet needs a column header label row but that should be fairly easily rectified.
You could use a function like this :
Sub test_atame()
Dim Ws As Worksheet
Set Ws = Sheet_Exists(ThisWorkbook, "Sheet1")
Set Ws = Sheet_Exists(ActiveWorkbook, "Sheet1")
End Sub
Here is the function :
Public Function Sheet_Exists(aWorkBook As Workbook, Sheet_Name As String) As Worksheet
Dim Ws As Worksheet, _
SExistS As Boolean
SExistS = False
For Each Ws In aWorkBook.Sheets
If Ws.Name <> Sheet_Name Then
Else
SExistS = True
Exit For
End If
Next Ws
If SExistS Then
Set Sheet_Exists = aWorkBook.Sheets(Sheet_Name)
Else
Set Sheet_Exists = Nothing
MsgBox "The sheet " & Sheet_Name & " wasn't found in " & aWorkBook.Name & vbCrLf & _
"Break code to check and correct.", vbCritical + vbOKOnly
End If
End Function
Maybe a check like:
Public Function SheetExists(ByVal Book As Workbook, ByVal SheetName As String) As Boolean
On Error Resume Next
Dim wsTest As Worksheet
Set wsTest = Book.Worksheets(SheetName)
If Not wsTest Is Nothing Then SheetExists = True
End Function
The attached code is all located in the excel VBAProject's module. The code will scan thru all of the existing worksheets and retrieve data, sort it, and even create new worksheets if a sub-assembly is found.
The problems are:
(1) it will not perform any task on the newly created worksheets until it is re-ran. I think the issue has to do with forcing the workbook to update it's worksheet list each time it creates a new worksheet.
(2) the routine seems to add a worksheet at end of run that does not match the criteria defined for creating new worksheets. (i.e. sub-assembly numbers start with either 772, 993, 995, 996, or 997)
Note that there is disabled code in sections so that I can keep track of a few of the things that I have tried such as - 'ThisWorkbook.Save , etc...
Any help would be appreciated, I'm running out of hair :)
Code:
Sub LoopThroughSheets()
Dim ws As Worksheet
Dim WS_Count As Integer
Dim ws_iCount As Integer
Dim i As Variant
Dim myBOMValue As Variant
Dim iRow As Long
Dim iRowValue As Variant
Dim iRowL As Variant
Dim iCountA As Integer
Dim sShtName As String
For Each ws In ActiveWorkbook.Worksheets
On Error Resume Next 'Will continue if an error results
If Not ws.Name = "Main" And Not ws.Name = "BOM" Then
myBOMValue = ws.Name
Sheets(ws.Name).Activate
' store sub-assembly name at cell C1 of active worksheet
Range("C1").Value = ws.Name
' Cmd for system and application to do non-macro related events
DoEvents
' Begin FishBowl Query for sub-assembly parts
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Array(Array("ODBC;DSN=Fishbowl;Driver=Firebird/InterBase(r) driver;Dbname=###.###.###.###:C:\Fishbowl2\database\data\$$$$.FDB;CHARSET=NONE;;UID=GO"), Array("NE;Client=C:\Program Files\Fishbowl\odbc\fbclient32.dll;")), Destination:=Range("$A$2")).QueryTable
' ## QueryTable commands START
' select BOM and retrieve data
.CommandText = Array("SELECT BOM.NUM, PART.NUM, PART.DESCRIPTION, BOMITEM.QUANTITY" & Chr(13) & Chr(10) & "FROM BOMITEM" & Chr(13) & Chr(10) & "INNER JOIN BOM" & Chr(13) & Chr(10) & "ON BOMITEM.BOMID = BOM.ID" & Chr(13) & Chr(10) & "INNER JOIN PART" & Chr(13) & Chr(10) & "ON PART.ID = BOMITEM.PARTID" & Chr(13) & Chr(10) & "WHERE BOM.NUM Like '%" & myBOMValue & "%'" & Chr(13) & Chr(10) & "Order BY Part.Num")
.RowNumbers = False
.FillAdjacentFormulas = False
.PreserveFormatting = True
.RefreshOnFileOpen = False
.BackgroundQuery = False
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = True
.RefreshPeriod = 0
.PreserveColumnInfo = True
.Refresh
' ## QueryTable commands END
End With
' Cmd for system and application to do non-macro related events
DoEvents
Application.ScreenUpdating = True
' *********************
' Begin duplicate part number consolidation
Application.ScreenUpdating = True
iRowL = Cells(Rows.Count, 1).End(xlUp).Row
'Cycle through all the cells in that column:
For iRow = 3 To iRowL
If Cells(iRow, 2) = Cells((iRow + 1), 2) Then
iCountA = 0
Do While (Cells(iRow, 2) = Cells((iRow + 1), 2)) And (IsEmpty(Cells(iRow, 1)) = False)
iRowValue = (Cells(iRow, 4) + Cells((iRow + 1), 4))
Cells(iRow, 4) = iRowValue
Rows(iRow + 1).EntireRow.Delete
iCountA = iCountA + 1
If iCountA > 20 Then
Exit Do
Else
End If
Loop
Else
End If
Next iRow
' Cmd for system and application to do non-macro related events
DoEvents
Application.ScreenUpdating = True
' Cmd for system and application to do non-macro related events
DoEvents
' *********************
' Reset variables and Begin checking for sub-assemblies
iRow = 0
iRowValue = 0
iRowL = 0
'Set up the count as the number of filled rows in the first column of Sheet1.
iRowL = Cells(Rows.Count, 1).End(xlUp).Row
'Cycle through all the cells in that column:
For iRow = 3 To iRowL
sShtName = Cells(iRow, 2).Value
If (InStr(1, Cells(iRow, 2).Value, "772") And Not WksExists(sShtName)) Then
Worksheets.Add after:=Worksheets(Worksheets.Count)
ActiveSheet.Name = sShtName
'Sheets(ws.Name).Activate
'ThisWorkbook.Save
ElseIf (InStr(1, Cells(iRow, 2).Value, "993") And Not WksExists(sShtName)) Then
Worksheets.Add after:=Worksheets(Worksheets.Count)
ActiveSheet.Name = sShtName
'Sheets(ws.Name).Activate
'ThisWorkbook.Save
ElseIf (InStr(1, Cells(iRow, 2).Value, "995") And Not WksExists(sShtName)) Then
Worksheets.Add after:=Worksheets(Worksheets.Count)
ActiveSheet.Name = sShtName
'Sheets(ws.Name).Activate
'ThisWorkbook.Save
ElseIf (InStr(1, Cells(iRow, 2).Value, "996") And Not WksExists(sShtName)) Then
Worksheets.Add after:=Worksheets(Worksheets.Count)
ActiveSheet.Name = sShtName
'Sheets(ws.Name).Activate
'ThisWorkbook.Save
ElseIf (InStr(1, Cells(iRow, 2).Value, "997") And Not WksExists(sShtName)) Then
Worksheets.Add after:=Worksheets(Worksheets.Count)
ActiveSheet.Name = sShtName
'Sheets(ws.Name).Activate
'ThisWorkbook.Save
Else
End If
'change active workbook sheet
Sheets(ws.Name).Activate
sShtName = ""
Next iRow
Else
End If
' Cmd for system and application to do non-macro related events
DoEvents
Application.ScreenUpdating = True
' change active workbook sheet back to Main
Sheets("Main").Activate
Next ws
End Sub
In general you want to try to avoid modifying any collection while you're at the same time looping over it.
You might find it easier to add all of the existing sheets to a Collection, and then process that by taking the first item from it, processing it and then removing it from the collection. End the loop when you've removed all items from the collection.
If you add one or more new sheets during the processing, then add those to the Collection to ensure they'll also get processed.
Here's a simple example of that approach:
Sub TestSheetLoop()
Dim colSheets As New Collection
Dim sht As Worksheet, shtNew As Worksheet
'grab all existing sheets
For Each sht In ThisWorkbook.Worksheets
colSheets.Add sht
Next sht
Do While colSheets.Count > 0
Set sht = colSheets(1)
Debug.Print sht.Name
'*********************
'...process this sheet
'*********************
'adding a new sheet...
If sht.Name = "Sheet2" Then
Set shtNew = ThisWorkbook.Sheets.Add()
shtNew.Name = "New sheet"
'add to collection
colSheets.Add shtNew
End If
'remove the sheet we just processed
colSheets.Remove (1)
Loop
End Sub
I am using the code below, to copy a hidden worksheet and copy it, rename is and fill in certain fields on two sheets.
I have done it like this, as i need to copy the layout and formatting of the hidden sheet.
The problem I am having, is that when i click the create button, if the sheet already exits, it completely crashes Excel, I have tried to add error handling but everything i have tried to check if the sheet exists doesn't work and still crashes Excel.
Have have separated the code that is un-hides the template sheet, copys it, renames the new sheet, and then re-hides the template.
What I would like it to do, is check the entered sheet name from TextBox5, and check is the sheet exists, if it does the display a message box, saying sheet already exists, if the sheet does not exist them carry on with the code as normal.
If really appreciate all the help and support i have already received, and thank all of you for the help you can provide with this.
Private Sub CommandButton3_Click()
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Sheets("Template")
Dim newws As Worksheet, sh As Worksheet, newname
Dim query As Long, xst As Boolean, info As String
Dim NextRow As Long, myCCName As Variant, lastRow2 As Long, lastRow As Long
'Contract Name
Dim Contact As String, name As String, name2 As String, SpacePos As Integer
Dim answer As Integer
With Application
.ScreenUpdating = False
.EnableEvents = False
.CutCopyMode = False
End With
lastRow2 = Sheets("Payment Form").Range("A18:A34").End(xlDown).Row
lastRow = Sheets("Payment Form").Range("U36:U53").End(xlDown).Row
'Contract Name
Set contract = Sheets("Payment Form").Range("C9")
SpacePos = InStr(contract, "- ")
name = Left(contract, SpacePos)
name2 = Right(contract, Len(contract) - Len(name))
'
retry:
xst = False
newname = Me.TextBox5.Value
myCCName = Me.TextBox4.Value
If newname = "" Then
MsgBox "You have not entered a CC Code Number. Please enter CC Code Number!", vbExclamation, "An Error Occured"
Exit Sub
End If
If myCCName = "" Then
MsgBox "You have not entered a CC Code Name. Please enter CC Code Name!", vbExclamation, "An Error Occured"
Exit Sub
End If
For Each sh In wb.Sheets
If sh.name = newname Then
xst = True: Exit For
End If
Next
If Len(newname) = 0 Or xst = True Then
info = "Sheet name is invalid. Please retry."
GoTo retry
End If
Sheets("Template").Visible = True
ws.Copy before:=Sheets("Details"): Set newws = ActiveSheet: newws.name = newname
Sheets("Template").Visible = False
With ActiveWorkbook.Sheets("Payment Form").Activate
For Each cell In Columns(1).Range("A18:A34").Cells
If Len(cell) = 0 Then cell.Select: Exit For
Next cell
ActiveCell.Value = newname & " " & "-" & name2 & ":" & " " & myCCName
End With
With ActiveWorkbook.Sheets(newname).Activate
ActiveWorkbook.Sheets(newname).Range("D4") = Sheets("Payment Form").Range("a18:a34").End(xlDown).Value
ActiveWorkbook.Sheets(newname).Range("D6") = Sheets("Payment Form").Range("L11").Value
ActiveWorkbook.Sheets(newname).Range("D8") = Sheets("Payment Form").Range("C9").Value
ActiveWorkbook.Sheets(newname).Range("D10") = Sheets("Payment Form").Range("C11").Value
End With
ActiveWorkbook.Sheets("Payment Form").Activate
With ActiveWorkbook.Sheets("Payment Form")
Range("J" & lastRow2 + 1) = 0
Range("L" & lastRow2 + 1).Formula = "=N" & lastRow2 + 1 & "-J" & lastRow2 + 1 & ""
Range("N" & lastRow2 + 1).Formula = "='" & newname & "'!L20"
Range("U" & lastRow + 1) = newname & ":" & " "
Range("V" & lastRow + 1).Formula = "='" & newname & "'!I21"
Range("W" & lastRow + 1).Formula = "='" & newname & "'!L23"
Range("X" & lastRow + 1).Formula = "='" & newname & "'!K21"
End With
answer = MsgBox("Would you like to create another sheet?", vbYesNo + vbQuestion, "New Sheet")
If answer = vbYes Then
Else
Unload Me
End If
With Application
.ScreenUpdating = True
.EnableEvents = True
.CutCopyMode = True
End With
Me.TextBox4.Value = ""
Me.TextBox5.Value = ""
End Sub
There appear to be a few general typos and a few errors with your 'With' statements throughout the code.
I have hopefully tidied up and recoded the function to work, but as it is untested I can't guarantee it will work off the bat.
I have also included the worksheet check function as a separate function
Private Sub CommandButton3_Click()
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsTemplate As Worksheet: Set wsTemplate = wb.Sheets("Template")
Dim wsPayment As Worksheet: Set wsPayment = wb.Sheets("Payment Form")
Dim wsNew As Worksheet
Dim NewName As String: NewName = Me.TextBox5.Value
Dim CCName As Variant: CCName = Me.TextBox4.Value
If NewName = "" Or CCName = "" Then
MsgBox "CC Code Name or Number missing. Please check details!", vbExclamation, "An Error Occured"
Exit Sub
End If
If WorksheetExists(NewName) Then
MsgBox "Sheet name already exists. Please retry!", vbExclamation, "An Error Occured"
Exit Sub
End If
With Application
.ScreenUpdating = False
.EnableEvents = False
.CutCopyMode = False
End With
Dim lastRow As Long: lastRow = wsPayment.Range("U36:U53").End(xlDown).Row
Dim lastRow2 As Long: lastRow2 = wsPayment.Range("A18:A34").End(xlDown).Row
'Contract Name
Dim Contract As String: Contract = Sheets("Payment Form").Range("C9").Value
Dim SpacePos As Integer: SpacePos = InStr(Contract, "- ")
Dim Name As String: Name = Left(Contract, SpacePos)
Dim Name2 As String: Name2 = Right(Contract, Len(Contract) - Len(Name))
wsTemplate.Visible = True
wsTemplate.Copy before:=Sheets("Details"): Set wsNew = ActiveSheet
wsTemplate.Visible = False
With wsPayment
For Each Cell In .Range("A18:A34")
If Len(Cell) = 0 Then
Cell.Value = NewName & " -" & Name2 & ": " & CCName
Exit For
End If
Next Cell
End With
With wsNew
.Name = NewName
.Range("D4").Value = wsPayment.Range("A18:A34").End(xlDown).Value
.Range("D6").Value = wsPayment.Range("L11").Value
.Range("D8").Value = wsPayment.Range("C9").Value
.Range("D10").Value = wsPayment.Range("C11").Value
End With
With wsPayment
.Range("J" & lastRow2 + 1).Value = 0
.Range("L" & lastRow2 + 1).Formula = "=N" & lastRow2 + 1 & "-J" & lastRow2 + 1 & ""
.Range("N" & lastRow2 + 1).Formula = "='" & NewName & "'!L20"
.Range("U" & lastRow + 1).Value = NewName & ": "
.Range("V" & lastRow + 1).Formula = "='" & NewName & "'!I21"
.Range("W" & lastRow + 1).Formula = "='" & NewName & "'!L23"
.Range("X" & lastRow + 1).Formula = "='" & NewName & "'!K21"
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
.CutCopyMode = True
End With
Dim Answer As Integer: Answer = MsgBox("Would you like to create another sheet?", _
vbYesNo + vbQuestion, "New Sheet")
If Answer = vbNo Then Unload Me
Me.TextBox4.Value = ""
Me.TextBox5.Value = ""
End Sub
Public Function WorksheetExists(ByVal WorksheetName As String) As Boolean
On Error Resume Next
WorksheetExists = (ThisWorkbook.Sheets(WorksheetName).Name <> "")
On Error GoTo 0
End Function
Personally i use the function below to check if a sheet allready exist in the workbook, in which case it returns True:
Public Function doItExist(strSheetName as String) As Boolean
Dim wsTest As Worksheet: Set wsTest = Nothing
On Error Resume Next
Set wsTest = ThisWorkbook.Worksheets(strSheetName)
On Error GoTo 0
If wsTest Is Nothing Then
doExist = False
Else
doExist = True
End If
End Function
I cannot seem to find the original source for the code, but I cannot take credit, it is mearly a modified version of some code I found on either SO, ozgrid or Mrexcel
EDIT:
Taking a closer look at your code, it seems you allready check for the existence of the sheetname in the xst variable. As far as I can see the user is not able to update the sheetname if is invalid, as the retry block will just keep looping?
under retry:
'### This bit essentially does the same as doSheetExist
For Each sh In wb.Sheets
If sh.name = newname Then
xst = True: Exit For
End If
Next
'###
If Len(newname) = 0 Or xst = True Then 'if you go for the doSheetExist, then the xst check is obsolete. Else move the xst check to the elseif and remove the doSheetExist call
info = "Sheet name is invalid. Please retry."
'GoTo retry 'As far as I can tell calling retry would just cause an infinite loop as the user have had no chance to update sheetname
Exit Sub 'let the user update and click the button again
ElseIf doSheetExist(newname) = True Then
info = "Sheet name allready exist. Please specify other sheetname"
Exit Sub
End If