I am trying to generate multiple invoices based on a template and an excel sheet of data and save it. The following code is throwing me a 1004 error - Application defined or object defined error. Can you please help. I am new to vba.
Sub AddNew()
Dim str1, str2, str3 As String
Dim numrows As Integer
Dim i As Integer
numrows = ActiveWorkbook.Sheets("Rawdata").Range("A" & Rows.Count).End(xlUp).Row - 2
MsgBox numrows
i = 3
While numrows > 0
str1 = ActiveWorkbook.Sheets("Rawdata").Cells(i, 16).Value
MsgBox (str1)
str2 = ActiveWorkbook.Sheets("Rawdata").Cells(i, 1).Value
'cannot save filename with backslash
str3 = Replace(ActiveWorkbook.Sheets("Rawdata").Cells(i, 2).Value, "/", "-")
Set NewBook = Workbooks.Add
With NewBook
.Title = "All Invoice"
.Subject = "Invoice"
.SaveAs Filename:="D:\Nandini\Invoice generation automation\" & str1 & " " & Format(str2, "mmm") & "-" & Format(str2, "YYYY") & " " & str3 & ".xlsx"
.Close SaveChanges:=True
End With
ActiveWorkbook.Sheets("Invoice").Select
Cells.Select
Selection.Copy
Workbooks.Open ("D:\Nandini\Invoice generation automation\" & str1 & " " & Format(str2,
"mmm") & "-" & Format(str2, "YYYY") & " " & str3 & ".xlsx")
activeworksheet.Paste
numrows = numrows - 1
i = i + 1
Wend
End Sub
Try below code :
Sub AddNew()
Dim str1, str2, str3 As String
Dim numrows As Integer
Dim i As Integer
Dim NewBook As Workbook, oWkb As Workbook
With ThisWorkbook.Sheets("Rawdata")
numrows = .Range("A" & .Rows.Count).End(xlUp).Row - 2
i = 3
While numrows > 0
str1 = .Cells(i, 16).Value
str2 = .Cells(i, 1).Value
'cannot save filename with backslash
str3 = Replace(.Cells(i, 2).Value, "/", "-")
Set NewBook = Workbooks.Add
With NewBook
.Title = "All Invoice"
.Subject = "Invoice"
.SaveAs Filename:="D:\Nandini\Invoice generation automation\" & str1 & " " & Format(str2, "mmm") & "-" & Format(str2, "YYYY") & " " & str3 & ".xlsx"
.Close SaveChanges:=True
End With
ThisWorkbook.Sheets("Invoice").Cells.Copy
Set oWkb = Workbooks.Open("D:\Nandini\Invoice generation automation\" & str1 & " " & Format(str2, "mmm") & "-" & Format(str2, "YYYY") & " " & str3 & ".xlsx")
oWkb.ActiveSheet.Range("A1").PasteSpecial
numrows = numrows - 1
i = i + 1
Wend
End With
End Sub
Related
Is it possible to open an existing application window?
What I want:
What is the code in order to put focus on an already open, but not in focus, application. For example, with:
Set objIE = New InternetExplorer
but I want the macro to put focus on an already existing IE.
Here is another case, I let Lotus notes create an email with the following code:
Sub Email_Bot()
'variables are defined
Dim Maildb As Object
Dim UserName As String
Dim MailDbName As String
Dim MailDoc As Object
Dim Session As Object
Dim AttachME As Object
Dim EmbedObj As Object
Dim Attachment As String
Dim stAttachment As String
Dim Mail_Form As String
Dim Mail_SendTo As String
Dim Mail_Subject As String
Dim Mail_Body As String
Dim Mail_Attachement As Boolean
Dim Mail_Save As Boolean
Dim Mail_Send As Boolean
Dim Mail_Name As String
Dim Mail_Text_1 As String
Dim Mail_Text_2 As String
Dim Mail_Text_3 As String
Dim Mail_Text_4 As String
Dim Mail_Text_5 As String
Dim Mail_Text_6 As String
Dim Mail_Closing As String
Dim Mail_SendBy As String
Dim tb_Mailing_List As Object
Dim tb_Email_Template As Object
Dim LastRow As Integer
Dim Row_Count As Integer
Dim Mail_Body_Lock As Boolean
Dim Workspace As Object
Const EMBED_ATTACHMENT As Long = 1454
'worksheets are defined
Set tb_Mailing_List = ThisWorkbook.Sheets("Mailing List")
Set tb_Email_Template = ThisWorkbook.Sheets("Email Template")
'mail session is defined
Set Session = CreateObject("Notes.NotesSession")
Set Maildb = Session.CURRENTDATABASE
Set MailDoc = Maildb.CREATEDOCUMENT
Set Workspace = CreateObject("Notes.NOTESUIWORKSPACE")
'important variables are set
LastRow = tb_Mailing_List.Cells(Rows.Count, 2).End(xlUp).Row
Row_Count = 3
Mail_Body_Lock = False
'cell assignment
Mail_Text_1 = tb_Email_Template.Cells(4, 4).Value
Mail_Text_2 = tb_Email_Template.Cells(5, 4).Value
Mail_Text_3 = tb_Email_Template.Cells(6, 4).Value
Mail_Text_4 = tb_Email_Template.Cells(7, 4).Value
Mail_Text_5 = tb_Email_Template.Cells(8, 4).Value
Mail_Text_6 = tb_Email_Template.Cells(9, 4).Value
Mail_Closing = tb_Email_Template.Cells(25, 4).Value
Mail_SendBy = tb_Email_Template.Cells(12, 4).Value & vbNewLine & vbNewLine & tb_Email_Template.Cells(13, 4).Value & vbNewLine & tb_Email_Template.Cells(14, 4).Value & vbNewLine & tb_Email_Template.Cells(15, 4).Value & vbNewLine & vbNewLine & tb_Email_Template.Cells(16, 4).Value & vbNewLine & tb_Email_Template.Cells(17, 4).Value & vbNewLine & tb_Email_Template.Cells(18, 4).Value & vbNewLine & tb_Email_Template.Cells(19, 4).Value & vbNewLine & tb_Email_Template.Cells(20, 4).Value & vbNewLine & tb_Email_Template.Cells(21, 4).Value & vbNewLine & tb_Email_Template.Cells(22, 4).Value
'loops until all names have been filled
Do Until Row_Count = LastRow + 1
'Mail Dashboard
Mail_Body_Lock = False
Mail_Send = False
Mail_Form = "Memo"
Mail_Name = tb_Mailing_List.Cells(Row_Count, 2).Value
Mail_SendTo = tb_Mailing_List.Cells(Row_Count, 4).Value
Mail_Subject = tb_Email_Template.Cells(2, 4).Value
Mail_Save = True
'exit round in case the email address is not present
If Mail_SendTo = "" Then GoTo NoEmail
'if only body row 1 has text
If Mail_Text_2 = "" And Mail_Text_3 = "" And Mail_Text_4 = "" And Mail_Text_5 = "" And Mail_Text_6 = "" Then
Mail_Body = "Dear " & Mail_Name & vbNewLine & vbNewLine & Mail_Text_1 & vbNewLine & vbNewLine & Mail_Closing & vbNewLine & vbNewLine & vbNewLine & Mail_SendBy
Mail_Body_Lock = True
End If
'if only body row 1 and row 2 have text
If Mail_Body_Lock = False And Mail_Text_3 = "" And Mail_Text_4 = "" And Mail_Text_5 = "" And Mail_Text_6 = "" Then
Mail_Body = "Dear " & Mail_Name & "," & vbNewLine & vbNewLine & Mail_Text_1 & vbNewLine & Mail_Text_2 & vbNewLine & vbNewLine & Mail_Closing & vbNewLine & vbNewLine & vbNewLine & Mail_SendBy
Mail_Body_Lock = True
End If
'if only body row 1 till row 3 have text
If Mail_Body_Lock = False And Mail_Text_4 = "" And Mail_Text_5 = "" And Mail_Text_6 = "" Then
Mail_Body = "Dear " & Mail_Name & vbNewLine & vbNewLine & Mail_Text_1 & vbNewLine & Mail_Text_2 & vbNewLine & Mail_Text_3 & vbNewLine & vbNewLine & Mail_Closing & vbNewLine & vbNewLine & vbNewLine & Mail_SendBy
Mail_Body_Lock = True
End If
'if only body row 1 till row 4 have text
If Mail_Body_Lock = False And Mail_Text_5 = "" And Mail_Text_6 = "" Then
Mail_Body = "Dear " & Mail_Name & vbNewLine & vbNewLine & Mail_Text_1 & vbNewLine & Mail_Text_2 & vbNewLine & Mail_Text_3 & vbNewLine & Mail_Text_4 & vbNewLine & vbNewLine & Mail_Closing & vbNewLine & vbNewLine & vbNewLine & Mail_SendBy
Mail_Body_Lock = True
End If
'if only body row 1 till row 5 have text
If Mail_Body_Lock = False And Mail_Text_6 = "" Then
Mail_Body = "Dear " & Mail_Name & vbNewLine & vbNewLine & Mail_Text_1 & vbNewLine & Mail_Text_2 & vbNewLine & Mail_Text_3 & vbNewLine & Mail_Text_4 & vbNewLine & Mail_Text_5 & vbNewLine & vbNewLine & Mail_Closing & vbNewLine & vbNewLine & vbNewLine & Mail_SendBy
Mail_Body_Lock = True
End If
'in case there is an error or something
If Mail_Body_Lock = False Then
Mail_Body = "Dear " & Mail_Name & vbNewLine & vbNewLine & Mail_Text_1 & vbNewLine & Mail_Text_2 & vbNewLine & Mail_Text_3 & vbNewLine & Mail_Text_4 & vbNewLine & Mail_Text_5 & vbNewLine & Mail_Text_6 & vbNewLine & vbNewLine & Mail_Closing & vbNewLine & vbNewLine & vbNewLine & Mail_SendBy
End If
'mail build-up
MailDoc.Form = Mail_Form
MailDoc.SendTo = Mail_SendTo
MailDoc.Subject = Mail_Subject
MailDoc.Body = Mail_Body
'attachement build-up
If tb_Email_Template.Cells(28, 4) <> "" And tb_Email_Template.Cells(29, 4) <> "" Then
Attachment = tb_Email_Template.Cells(28, 4)
stAttachment = tb_Email_Template.Cells(29, 4)
Set AttachME = MailDoc.CREATERICHTEXTITEM("stAttachment")
Set EmbedObj = AttachME.EmbedObject(EMBED_ATTACHMENT, "", Attachment, "stAttachment")
End If
Call Workspace.EDITDOCUMENT(True, MailDoc).GOTOFIELD("Body")
MsgBox "Email send?"
NoEmail:
Row_Count = Row_Count + 1
Loop
'variable dump
Set Maildb = Nothing
Set MailDoc = Nothing
Set AttachME = Nothing
Set Session = Nothing
Set EmbedObj = Nothing
End Sub
After:
Call Workspace.EDITDOCUMENT(True, MailDoc).GOTOFIELD("Body")
I want to VBA to open that window and not that I have to go there by myself. I am sure that there has to be a way. I used mouse movements, which worked until a colleague with a different screen res. used the program.
I am quite new to VBA and programming and taught myself so I am sorry if this is maybe a dumb question, but I couldn't find the answer so far anywhere else.
Try This way,
Public vPID As Variant
Public Sub OpenApplication()
'Launch application if not already open
If vPID = 0 Then 'Application not already open
101:
vPID = Shell("C:\Windows\system32\notepad.exe", vbNormalFocus)
Else 'Application already open so reactivate
On Error GoTo 101
AppActivate (vPID)
End If
End Sub
Because the variable vPID is stored as a project level Public Variable, its value will be retained for as long as your instance of Excel (or other Microsoft Office application) is open.
Line 22 is throwing the error (Set wbPath2)
This code is supposed to loop through each worksheet in my workbook and, as it loops, open another workbook related to the current loop iteration, then sum a column, then put that SUM in my original workbook. I'm getting and object error 91. I've been scratching my head for a while. Anyone know why this error message appears?
Private Sub PopulateData_Click()
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim lastDay As Long
lastDay = Day(WorksheetFunction.EoMonth(ComboBox1.Value & Year(Date), 0))
monthNumber = Month(DateValue("01-" & ComboBox1.Value & "-1900"))
Root = "C:\myDirectory\" & Year(Date) & "\" &
monthNumber & ". " & ComboBox1.Value & " " & Year(Date) & "\"
'TOTAL CARS PER WEEK
Dim wbPath2 As Object
sourceFile = monthNumber & ". " & ComboBox1.Value & " " & Year(Date)
sourceSheet = "\[" & ws.Name & " " & monthNumber & "." & lastDay & "." &
Format(Now(), "yy") & ".csv]"
For Each ws In ThisWorkbook.Sheets
If (ws.Name <> "Master") And (ws.Name <> "Combined") Then
Set wbPath2 = Workbooks.Open(Root & ws.Name & " " & monthNumber &
"." & lastDay & "." & Format(Now(), "yy") & ".csv")
With ws
.Cells(Application.WorksheetFunction.Match("Total cars per
week", Range("A:A"), 0), 18).Formula = "=SUM('" & Root &
sourceFile & sourceSheet & ws.Name & " " & monthNumber & "." &
lastDay & "." & Format(Now(), "yy") & "'!$H:$H)"
End With
wbPath2.Close
MsgBox wbPath2
End If
Next
Application.ScreenUpdating = True
End Sub
I had to Set the ws object to resolve run time 91 error. Look in the comments section for Mat's Mug's additional bug fixes.
Private Sub PopulateData_Click()
Application.ScreenUpdating = False
Dim ws As Worksheet
Set ws = ThisWorkbook.Activesheet
'...
I am currently working on parsing data from multiple worksheets within multiple workbooks into a summary worksheet. I have been able to select certain cells from all sheets and workbooks but would like to extract a range of columns if possible. How can I add this option to my loop condition?
for example If I have a worksheet called "Monday" and I would like to extract the cell range A2 through C57 and add it to my newly created worksheet.
Option Explicit
Sub GetMyData()
Dim myDir As String, fn As String, SheetName As String, SheetName2 As String, SheetName3 As String, n As Long, NR As Long
'***** Change Folder Path *****
myDir = "C:\attach"
'***** Change Sheetname(s) *****
SheetName = "Title"
SheetName2 = "Total"
SheetName3 = "Monday"
'***Loops through specified directory and parces data from each worksheet within each workbook by selecting specified .
fn = Dir(myDir & "\*.xlsx")
Do While fn <> ""
If fn <> ThisWorkbook.Name Then
With ThisWorkbook.Sheets("ImportTable")
NR = .Cells(Rows.Count, 1).End(xlUp).Row + 1
'Pick cells from worksheet "Title"
With .Range("A" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName & "'!A1"
.Value = .Value
End With
With .Range("B" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName & "'!A2"
.Value = .Value
End With
With .Range("C" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName & "'!B4"
.Value = .Value
End With
With .Range("D" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName & "'!B5"
.Value = .Value
End With
With .Range("E" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName & "'!B6"
.Value = .Value
End With
With .Range("F" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName & "'!B7"
.Value = .Value
End With
With .Range("G" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName2 & "'!B26"
.Value = .Value
End With
With .Range("H" & NR)
.Formula = "='" & myDir & "\[" & fn & "]" & SheetName2 & "'!A1"
.Value = .Value
End With
End With
End If
fn = Dir
Loop
ThisWorkbook.Sheets("ImportTable").Columns.AutoFit
End Sub
If you move your link creation to a separate sub your code will be more concise, and you can have the sub automatically adjust the type of formula (regular for single cells, or array formula for blocks of cells)
Sub tester()
Dim rng As Range
Set rng = ActiveSheet.Range("A2")
LinkToFile "C:\_Stuff\test", "temp report.xlsx", "Sheet1", "A1:D20", rng
Set rng = ActiveSheet.Range("F2")
LinkToFile "C:\_Stuff\test", "temp report.xlsx", "Sheet1", "A1", rng
End Sub
Sub LinkToFile(fPath As String, fName As String, shtName As String, _
addr As String, rngInsert As Range)
Dim rngTmp As Range, f As String
If Right(fPath, 1) <> "\" Then fPath = fPath & "\" 'win only!
f = "='" & fPath & "[" & fName & "]" & shtName & "'!" & addr
'linking to a range, or a single cell ?
If InStr(addr, ":") > 0 Then
Set rngTmp = rngInsert.Parent.Range(addr) 'to get num rows/cols
rngInsert.Resize(rngTmp.Rows.Count, rngTmp.Columns.Count).FormulaArray = f
Else
rngInsert.Formula = f
End If
End Sub
I was working with optimisation of code and after review from man people asked me to use Option Explicit and define Variables for everything and shorten the code. Which i did to maximum possible But the below code copies data from another excel by asking path and copy some specific data in column V and W. Also there is formula which compare data and find exact rows and which need to be copy.
Now please help how should i optimise this code and give variables to it.
Or please provide code in which we can compare 2 excel for example: A2:E is same then it should copy H2:I
For Each ws In MainWB.Worksheets
If ws.Name <> "Sap Data" And ws.Name <> "Automated BL Import" Then
With MainWB.Worksheets(ws.Name)
.Range("V1").Value = "When it will be Cleared or Action Taken/Required"
.Range("W1").Value = "Backup Link"
LastRow = MainWB.Worksheets(ws.Name).Range("B" & Rows.Count).End(xlUp).Row
.Range("Q1:Q" & LastRow).Delete
End With
End If
Next ws
b = MsgBox("Do you want to update comments for current postings from previous month?" & vbCrLf & vbCrLf & "Note:- If are runing this macro for the 1st time plese choose option 'No'", _
vbYesNo + vbQuestion, "Question")
If b = vbYes Then
Filename = Application.GetOpenFilename(, , "Please select previous month BL comment file to update comments.", , False)
If Filename <> "False" Then
Workbooks.Open Filename, Format:=2
End If
updatesheet = ActiveWorkbook.Name
For Each ws In MainWB.Sheets
If ws.Name <> "Sap Data" And ws.Name <> "Automated BL Import" Then
For Each ds In Workbooks(updatesheet).Sheets
If ds.Name = ws.Name Then
LastRow = MainWB.Worksheets(ws.Name).Range("B" & Rows.Count).End(xlUp).Row
With MainWB.Worksheets(ws.Name)
.Range("T2:T" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-1],'[" & updatesheet & "]" & ws.Name & "'!R2C[-1]:R1048576C,2,0) = 0,"""",VLOOKUP(RC[-1],'[" & updatesheet & "]" & ws.Name & "'!R2C[-1]:R1048576C,2,0)),"""")"
.Range("U2:U" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-2],'[" & updatesheet & "]" & ws.Name & "'!R2C[-2]:R1048576C,3,0) = 0,"""",VLOOKUP(RC[-2],'[" & updatesheet & "]" & ws.Name & "'!R2C[-2]:R1048576C,3,0)),"""")"
.Range("V2:V" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-3],'[" & updatesheet & "]" & ws.Name & "'!R2C[-3]:R1048576C,4,0) = 0,"""",VLOOKUP(RC[-3],'[" & updatesheet & "]" & ws.Name & "'!R2C[-3]:R1048576C,4,0)),"""")"
.Range("W2:W" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-4],'[" & updatesheet & "]" & ws.Name & "'!R2C[-4]:R1048576C,5,0) = 0,"""",VLOOKUP(RC[-4],'[" & updatesheet & "]" & ws.Name & "'!R2C[-4]:R1048576C,5,0)),"""")"
.Range("X2:X" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-5],'[" & updatesheet & "]" & ws.Name & "'!R2C[-5]:R1048576C,6,0) = 0,"""",VLOOKUP(RC[-5],'[" & updatesheet & "]" & ws.Name & "'!R2C[-5]:R1048576C,6,0)),"""")"
.Range("T2:X" & LastRow).Value = MainWB.Worksheets(ws.Name).Range("T2:X" & LastRow).Value
End With
Your bottom part is a mess, you are missing some end ifs, You are missing the dims for the variables
The first part of the code is below.
You need to explain what you are trying to do with the second part of the code.
Sub Button1_Click()
Dim wb As Workbook, ws As Worksheet
Dim bk As Workbook, sh As Worksheet
Set wb = Workbooks("ThisOne.xlsm")
For Each ws In wb.Sheets
If ws.Name <> "Sap Data" And ws.Name <> "Automated BL Import" Then
With ws
.Range("V1").Value = "When it will be Cleared or Action Taken/Required"
.Range("W1").Value = "Backup Link"
LastRow = ws.Range("B" & Rows.Count).End(xlUp).Row
.Range("Q1:Q" & LastRow).Delete'?
End With
End If
Next ws
b = MsgBox("Do you want to update comments for current postings from previous month?" & vbCrLf & vbCrLf & "Note:- If are runing this macro for the 1st time plese choose option 'No'", _
vbYesNo + vbQuestion, "Question")
If b = vbYes Then
Filename = Application.GetOpenFilename(, , "Please select previous month BL comment file to update comments.", , False)
If Filename <> "False" Then
Workbooks.Open Filename, Format:=2
End If
Else: Exit Sub
End If
Set bk = ActiveWorkbook
' updatesheet = ActiveWorkbook.Name'what is this for?
For Each sh In bk.Sheets
' If sh.Name <> "Sap Data" And ws.Name <> "Automated BL Import" Then
' For Each ds In Workbooks(updatesheet).Sheets
' If ds.Name = ws.Name Then
' LastRow = MainWB.Worksheets(ws.Name).Range("B" & Rows.Count).End(xlUp).Row
' With MainWB.Worksheets(ws.Name)
' .Range("T2:T" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-1],'[" & updatesheet & "]" & ws.Name & "'!R2C[-1]:R1048576C,2,0) = 0,"""",VLOOKUP(RC[-1],'[" & updatesheet & "]" & ws.Name & "'!R2C[-1]:R1048576C,2,0)),"""")"
' .Range("U2:U" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-2],'[" & updatesheet & "]" & ws.Name & "'!R2C[-2]:R1048576C,3,0) = 0,"""",VLOOKUP(RC[-2],'[" & updatesheet & "]" & ws.Name & "'!R2C[-2]:R1048576C,3,0)),"""")"
' .Range("V2:V" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-3],'[" & updatesheet & "]" & ws.Name & "'!R2C[-3]:R1048576C,4,0) = 0,"""",VLOOKUP(RC[-3],'[" & updatesheet & "]" & ws.Name & "'!R2C[-3]:R1048576C,4,0)),"""")"
' .Range("W2:W" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-4],'[" & updatesheet & "]" & ws.Name & "'!R2C[-4]:R1048576C,5,0) = 0,"""",VLOOKUP(RC[-4],'[" & updatesheet & "]" & ws.Name & "'!R2C[-4]:R1048576C,5,0)),"""")"
' .Range("X2:X" & LastRow).Formula = "=IFERROR(IF(VLOOKUP(RC[-5],'[" & updatesheet & "]" & ws.Name & "'!R2C[-5]:R1048576C,6,0) = 0,"""",VLOOKUP(RC[-5],'[" & updatesheet & "]" & ws.Name & "'!R2C[-5]:R1048576C,6,0)),"""")"
' .Range("T2:X" & LastRow).Value = MainWB.Worksheets(ws.Name).Range("T2:X" & LastRow).Value
' End With
' End If
' Next ds
' End If
Next sh
End Sub
I've created a macro that's meant to created a lump of CSS & HTML from a set of values in each sheet of a spreadsheet.
It's a little untidy as I created the function to write it from one sheet first as a proof of concept, and then updated it.
It doesn't throw any obvious errors, but the output varies, sometimes it shows the same thing both times, and then depending on where I've got debug MsgBoxs or watches in VBA seems to alter the output.
Any ideas what on earth i'm doing wrong?
Sub createCode()
Dim myWorkbook As Workbook
Dim mySheet As Worksheet
Set myWorkbook = Application.ActiveWorkbook
For Each mySheet In myWorkbook.Worksheets
Dim bannerCount As Integer
Dim BannerCollection() As Banner
Dim r As Range
Dim lastRow, lastCol
Dim allCells As Range
bannerCount = 0
lastCol = mySheet.Range("a2").End(xlToRight).Column
lastRow = mySheet.Range("a2").End(xlDown).Row
Set allCells = mySheet.Range("a2", mySheet.Cells(lastRow, lastCol))
' MsgBox (mySheet.Name)
' MsgBox ("lastRow:" & lastRow & "lastCol:" & lastCol)
ReDim BannerCollection(allCells.Rows.Count)
For Each r In allCells.Rows
Dim thisBanner As Banner
thisBanner.imagePath = ""
thisBanner.retImagePath = ""
thisBanner.bannerTitle = ""
thisBanner.urlPath = ""
bannerCount = bannerCount + 1
' MsgBox (bannerCount)
thisBanner.imagePath = Cells(r.Row, 2).Value
thisBanner.retImagePath = Cells(r.Row, 3).Value
thisBanner.bannerTitle = Cells(r.Row, 4).Value
thisBanner.urlPath = Cells(r.Row, 5).Value
'MsgBox (Cells(r.Row, 2).Value)
'MsgBox (Cells(r.Row, 3).Value)
'MsgBox (Cells(r.Row, 4).Value)
'MsgBox (Cells(r.Row, 5).Value)
BannerCollection(bannerCount - 1) = thisBanner
Next r
Dim i As Variant
Dim retinaCSS, imgCSS, firstBannerCode, otherBannersCode, bannerTracking As String
retinaCSS = ""
imgCSS = ""
firstBannerCode = ""
otherBannersCode = ""
bannerTracking = ""
For i = 0 To bannerCount - 1
bannerTracking = BannerCollection(i).bannerTitle
bannerTracking = Replace(bannerTracking, " ", "+")
bannerTracking = Replace(bannerTracking, "&", "And")
bannerTracking = Replace(bannerTracking, "%", "PC")
bannerTracking = Replace(bannerTracking, "!", "")
bannerTracking = Replace(bannerTracking, "£", "")
bannerTracking = Replace(bannerTracking, ",", "")
bannerTracking = Replace(bannerTracking, "'", "")
bannerTracking = Replace(bannerTracking, "#", "")
bannerTracking = Replace(bannerTracking, ".", "")
retinaCSS = retinaCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).retImagePath & "');}" & vbNewLine
imgCSS = imgCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).imagePath & "');}" & vbNewLine
If i = 0 Then
firstBannerCode = firstBannerCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
firstBannerCode = firstBannerCode & "" & vbNewLine
firstBannerCode = firstBannerCode & "</div>" & vbNewLine
Else
otherBannersCode = otherBannersCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
otherBannersCode = otherBannersCode & "" & vbNewLine
otherBannersCode = otherBannersCode & "</div>" & vbNewLine
End If
' MsgBox (BannerCollection(i).retImagePath & vbNewLine & BannerCollection(i).imagePath & vbNewLine & BannerCollection(i).bannerTitle & vbNewLine & BannerCollection(i).urlPath)
Next i
CodeString = ""
CodeString = CodeString & "<style type=" & Chr(34) & "text/css" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & "/* Banners */" & vbNewLine
CodeString = CodeString & imgCSS
CodeString = CodeString & "/* Retina Banners */" & vbNewLine
CodeString = CodeString & "#media only screen and (-webkit-min-device-pixel-ratio: 2) {" & vbNewLine
CodeString = CodeString & retinaCSS
CodeString = CodeString & "}" & vbNewLine
CodeString = CodeString & "</style>" & vbNewLine
CodeString = CodeString & "<div id=" & Chr(34) & "sliderTarget" & Chr(34) & " class=" & Chr(34) & "slides" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & firstBannerCode
CodeString = CodeString & "</div>" & vbNewLine
CodeString = CodeString & "<script id=" & Chr(34) & "sliderTemplate" & Chr(34) & " type=" & Chr(34) & "text/template" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & otherBannersCode
CodeString = CodeString & "</script>"
FilePath = Application.DefaultFilePath & "\" & mySheet.Name & "code.txt"
Open FilePath For Output As #2
Print #2, CodeString
Close #2
MsgBox ("code.txt contains:" & CodeString)
MsgBox (Application.DefaultFilePath & "\" & mySheet.Name & "code.txt")
Erase BannerCollection
Next mySheet
End Sub
and here is the Banner type:
Public Type Banner
imagePath As String
retImagePath As String
urlPath As String
bannerTitle As String
End Type
I ended up doing a bit of a code review (oops spent too much time on the Code Review site). I'll post this here in addition to #Jeeped answer in case you get some value from it.
Option Explicit
You should specify Option Explicit at the top of each code module. What this does is tell the VBA compiler to check that every variable that you are trying to use has been declared (i.e. you've got Dim blah as String, Public blah as String or Private blah as String for each blah you're using).
If you attempt to use a variable which hasn't been declared, the compiler will give you a compilation error where the first problem occurs. This helps if you mistype a variable name, otherwise the compiler will think you are talking about something new.
Adding this to the top of your code requires a couple of declarations in your code but nothing major.
Multiple variable declaration on a single line
Don't do it. You have the following line: Dim retinaCSS, imgCSS, firstBannerCode, otherBannersCode, bannerTracking As String which declares 5 variables. The first 4 are declared as variants and the last one is a String. Now your code may work like this but you were probably expecting all 5 to be Strings. Other languages I believe do operate this way but VBA doesn't.
Declare them separately like:
Dim retinaCSS As String
Dim imgCSS As String
Dim firstBannerCode As String
Dim otherBannersCode As String
Dim bannerTracking As String
Don't initialise variables unnecessarily
I see code like:
CodeString = ""
CodeString = CodeString & "<style type=" & Chr(34) & "text/css" & Chr(34) & ">" & vbNewLine
Now the problem with this is that you're assigning the empty string value to CodeString but then you are immediately assigning something else to it in the very next line. The risk is that you might try to use a variable before you have assigned something to it. This isn't a risk for the string type since it implicitly assigned an empty string value when it is created.
You can safely remove the first assignment to it. The danger could come from object references. Say if you have a reference to a worksheet but do not assign a worksheet to the variable before you try to use it. In any case you want to make sure that your variable has the required value before you attempt to use the value it holds.
Use Collection instead of an array
The array code is cumbersome and inflexible. VBA has a simple collection type which allows you to add and remove items to and from it without having to declare a fixed size.
You can also iterate through the contents using a For Each loop.
Here is the code I'm recommending:
Dim BannerCollection As Collection
Set BannerCollection = New Collection
' ...
For Each r In allCells.Rows
Dim thisBanner As Banner
Set thisBanner = New Banner
' ...
BannerCollection.Add thisBanner
Next r
' ...
Dim b As Banner
For Each b In BannerCollection
' do something with the banner.
Next
Now to do this, Banner must be a Class not a Type. I think it makes life a lot easier though.
Split a big method up into single purpose methods.
For instance I extracted a method as follows:
Private Function UrlEncode(ByVal text As String) As String
text = Replace(text, " ", "+")
text = Replace(text, "&", "And")
text = Replace(text, "%", "PC")
text = Replace(text, "!", "")
text = Replace(text, "£", "")
text = Replace(text, ",", "")
text = Replace(text, "'", "")
text = Replace(text, "#", "")
text = Replace(text, ".", "")
UrlEncode = text
End Function
Now this can be referenced like bannerTracking = UrlEncode(b.bannerTitle).
You are setting allCells to a distinct range of cells correctly.
Set allCells = mySheet.Range("a2", mySheet.Cells(lastRow, lastCol))
Then you loop through each row in the allCells range.
For Each r In allCells.Rows
But when you actually go to use r, it is only to use the row number.
thisBanner.imagePath = Cells(r.Row, 2).Value
r.Row is a number between 1 and 1,048,576, nothing more. There is no guarantee that Cells(r.Row, 2).Value refers to something on mySheet; only that whatever worksheet it is coming from it will using whatever worksheet's row number that corresponds to r.row. You need to define some parentage. An With ... End With block within the For ... Next and properly annotated .Range and .Cell references should suffice.
Sub createCode()
Dim myWorkbook As Workbook
Dim mySheet As Worksheet
Dim bannerCount As Integer
Dim BannerCollection() As Banner
Dim r As Range
Dim lastRow, lastCol
Dim allCells As Range
Set myWorkbook = Application.ActiveWorkbook
For Each mySheet In myWorkbook.Worksheets
With mySheet
'declare your vars outside the loop and zero/null then here if necessary.
bannerCount = 0
lastCol = .Range("a2").End(xlToRight).Column
lastRow = .Range("a2").End(xlDown).Row
Set allCells = .Range("a2", .Cells(lastRow, lastCol))
' MsgBox (mySheet.Name)
' MsgBox ("lastRow:" & lastRow & "lastCol:" & lastCol)
ReDim BannerCollection(allCells.Rows.Count)
For Each r In allCells.Rows
Dim thisBanner As Banner
thisBanner.imagePath = ""
thisBanner.retImagePath = ""
thisBanner.bannerTitle = ""
thisBanner.urlPath = ""
bannerCount = bannerCount + 1
' MsgBox (bannerCount)
thisBanner.imagePath = .Cells(r.Row, 2).Value
thisBanner.retImagePath = .Cells(r.Row, 3).Value
thisBanner.bannerTitle = .Cells(r.Row, 4).Value
thisBanner.urlPath = .Cells(r.Row, 5).Value
'MsgBox (.Cells(r.Row, 2).Value)
'MsgBox (.Cells(r.Row, 3).Value)
'MsgBox (.Cells(r.Row, 4).Value)
'MsgBox (.Cells(r.Row, 5).Value)
BannerCollection(bannerCount - 1) = thisBanner
Next r
Dim i As Variant
Dim retinaCSS, imgCSS, firstBannerCode, otherBannersCode, bannerTracking As String
retinaCSS = ""
imgCSS = ""
firstBannerCode = ""
otherBannersCode = ""
bannerTracking = ""
For i = 0 To bannerCount - 1
bannerTracking = BannerCollection(i).bannerTitle
bannerTracking = Replace(bannerTracking, " ", "+")
bannerTracking = Replace(bannerTracking, "&", "And")
bannerTracking = Replace(bannerTracking, "%", "PC")
bannerTracking = Replace(bannerTracking, "!", "")
bannerTracking = Replace(bannerTracking, "£", "")
bannerTracking = Replace(bannerTracking, ",", "")
bannerTracking = Replace(bannerTracking, "'", "")
bannerTracking = Replace(bannerTracking, "#", "")
bannerTracking = Replace(bannerTracking, ".", "")
retinaCSS = retinaCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).retImagePath & "');}" & vbNewLine
imgCSS = imgCSS & "#sliderTarget .banner-" & i + 1 & "{background-image: url('/assets/static/" & BannerCollection(i).imagePath & "');}" & vbNewLine
If i = 0 Then
firstBannerCode = firstBannerCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
firstBannerCode = firstBannerCode & "" & vbNewLine
firstBannerCode = firstBannerCode & "</div>" & vbNewLine
Else
otherBannersCode = otherBannersCode & "<div class=" & Chr(34) & "banner banner-" & i + 1 & " staticBanner" & Chr(34) & ">" & vbNewLine
otherBannersCode = otherBannersCode & "" & vbNewLine
otherBannersCode = otherBannersCode & "</div>" & vbNewLine
End If
' MsgBox (BannerCollection(i).retImagePath & vbNewLine & BannerCollection(i).imagePath & vbNewLine & BannerCollection(i).bannerTitle & vbNewLine & BannerCollection(i).urlPath)
Next i
CodeString = ""
CodeString = CodeString & "<style type=" & Chr(34) & "text/css" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & "/* Banners */" & vbNewLine
CodeString = CodeString & imgCSS
CodeString = CodeString & "/* Retina Banners */" & vbNewLine
CodeString = CodeString & "#media only screen and (-webkit-min-device-pixel-ratio: 2) {" & vbNewLine
CodeString = CodeString & retinaCSS
CodeString = CodeString & "}" & vbNewLine
CodeString = CodeString & "</style>" & vbNewLine
CodeString = CodeString & "<div id=" & Chr(34) & "sliderTarget" & Chr(34) & " class=" & Chr(34) & "slides" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & firstBannerCode
CodeString = CodeString & "</div>" & vbNewLine
CodeString = CodeString & "<script id=" & Chr(34) & "sliderTemplate" & Chr(34) & " type=" & Chr(34) & "text/template" & Chr(34) & ">" & vbNewLine
CodeString = CodeString & otherBannersCode
CodeString = CodeString & "</script>"
FilePath = Application.DefaultFilePath & "\" & mySheet.Name & "code.txt"
Open FilePath For Output As #2
Print #2, CodeString
Close #2
MsgBox ("code.txt contains:" & CodeString)
MsgBox (Application.DefaultFilePath & "\" & mySheet.Name & "code.txt")
Erase BannerCollection
End With
Next mySheet
End Sub