Pass argument with Vba excel in .onAction in excel 2016 - excel

I am Trying to pass the value i.e sArg with the onAction.But Im not able to do so.
I have tried like this: "'btnT """ & sArg & """'" But this doesn't work.
For i = 3 To LastRow Step 1
Set t2 = ActiveSheet.Range(Cells(i, LastCol + 3), Cells(i, LastCol + 3))
Set btn2 = ActiveSheet.Buttons.Add(t2.Left, t2.Top, t2.Width, t2.Height)
sArg = CStr(i)
With btn2
.OnAction = "Sheet1.btnT"
.Caption = "View " & i
.Name = CStr(i)
End With
Next i
Then the function is
Sub btnT(Text)
MsgBox Text
Exit Sub

You just have to enclose the single and possibly double quotes.
If the argument is numeric (eg 1), use
.OnAction = "'SubName 1'"
If the argument is a string then use
.OnAction = "'SubName ""SomeText""'"
Sub Demo()
Dim i As Long
Dim Arg As Variant
Dim t2 As Range
Dim btn2 As Button
For i = 3 To 4 Step 1
Set t2 = ActiveSheet.Range(Cells(i, LastCol + 3), Cells(i, LastCol + 3))
Set btn2 = ActiveSheet.Buttons.Add(t2.Left, t2.Top, t2.Width, t2.Height)
Arg = i '<~~ adjust to suit your needs
With btn2
.Caption = "View " & i
.Name = CStr(i)
If IsNumeric(Arg) Then
.OnAction = "'Sheet1.btnT " & Arg & "'"
Else
.OnAction = "'Sheet1.btnT """ & Arg & """'"
End If
End With
Next i
End Sub
Sub btnT(Arg As Variant)
MsgBox Arg
End Sub

Related

How do I split the output of an excel report when collecting input with a user form and exporting to PDF?

I am trying to create a front-end form that can be filled out by a user, which will then populate a back-end spreadsheet the user cannot edit. This spreadsheet needs to follow a standard layout that I have already created, and the form will populate predetermined cells (or will create cells following the layout) based on user input from the form. I also need the form to be able to add/repeat questions, as well as duplicate sections in the spreadsheet based on user input requirements ("Do you need to add a section? [Yes/No]" //If [Yes], then duplicate section, repeat questions in form; if [No], then export data to spreadsheet, exit form). The spreadsheet layout consists of sections. The number of sections/cells per section required will vary from user to user, but the type of data will typically remain the same, and therefore should be able to choose the name of the field from a list of some sort (drop down?) However, the user may need to create a custom name for a field. I also need the form to be able to automatically adjust cell sizes based on the amount of text for that value. It is important that this form is printable, and does not separate sections from page to page. Furthermore, each "Notes" field will vary in size, and should be automatically resized to only show the existing text, plus one blank line for hand-written notes. I would also like the form to prompt the user to indicate whether a field is needed (some fields will be permanent, and the user will not be prompted about these fields). If the field is not needed, I need the form to exclude it from the final output.
I am open to suggestions on how to design this project differently than I have already (for example, using alternate programs/software/coding languages).
So far, however, I have created a user form in Excel that collects data and inputs it into a spreadsheet, which can then be exported to a PDF report using a set format/layout on another sheet of the workbook. The layout consists of a "Header" section and an "Item" section. In the form, the user can input a single job number and multiple item numbers, and when they export the report, the code prints the job number in the provided layout for the header, and then loops through the item numbers and copies and populates the item section for as many items as the user has provided. In the exported PDF, however, the item section gets split between pages, regardless of page orientation (landscape orientation is preferred). How do I prevent the "Item" section from being broken up between pages?
Option Explicit
Dim ctl As Control
Dim rCell As Range
Dim img As Picture
Dim newrow As ListRow
Dim tbl1 As ListObject
Dim msgValue As VbMsgBoxResult
Dim ary As Variant, aryx As Variant
Dim ws2 As Worksheet, ws1 As Worksheet
Dim s As String, FilePath As String, user As String, pFilename As String, part As String
Dim x As Long, sc As Long, j As Long, ctr As Long, rctr As Long, tbl1row As Long, r As Long, t As Long, items As Long, y As Long, ctrx As Long
Private Sub cmdCLEAR_Click()
MsgBox ("This action only clears the form NOT the record" & vbCrLf & "Ready for adding NEW entry."), vbOKOnly, "Clear Form "
CLEARFORM
Me.cmdADD.Enabled = True
Me.TextBox6.SetFocus
End Sub
Sub CLEARFORM()
For Each ctl In Me.Controls
Select Case TypeName(ctl)
Case "TextBox"
ctl.Text = ""
Case "ComboBox"
ctl.ListIndex = -1
ctl.Value = ""
End Select
Next ctl
Me.Image1.Picture = LoadPicture("") '*********clears picture******
End Sub
Private Sub cmdADD_Click()
For x = 1 To 35
If Controls("TextBox" & x).Text = "" Then
MsgBox "Data field missing", vbCritical, "Data missing"
Exit Sub
End If
Next x
msgValue = MsgBox("Do want to add another item?", vbYesNo + vbQuestion, "Next Item ?")
If msgValue = vbYes Then
SAVEDATANEXT
For x = 16 To 35
Me.Controls("TextBox" & x).Text = ""
Next x
Me.TextBox6.SetFocus
Exit Sub
End If
SAVEDATA
LOADLIST
LOADCOMBO1
End Sub
Sub SAVEDATANEXT()
Set newrow = tbl1.ListRows.Add
With newrow
For x = 1 To 35
.Range(x) = Me.Controls("TextBox" & x).Text '***************textbox35 contains picture filepath ***********
Next x
.Range(36) = tbl1.ListRows.Count '*********this is important row counter saves the need for search routines***********
End With
End Sub
Sub SAVEDATA()
Set newrow = tbl1.ListRows.Add
With newrow
For x = 1 To 35
.Range(x) = Me.Controls("TextBox" & x).Text '***************textbox35 contains picture filepath ***********
Next x
.Range(36) = tbl1.ListRows.Count '*********this is important row counter saves the need for search routines***********
End With
CLEARFORM
LOADLIST
End Sub
Sub LOADLIST()
Set ws2 = Sheet2
Set tbl1 = ws2.ListObjects("Table1")
With tbl1
If .DataBodyRange.Cells(1, 1) = vbNullString Then Exit Sub
ary = .DataBodyRange
End With
Me.ListBox1.List = ary
End Sub
Private Sub cmdPRINT_Click()
If Me.ComboBox1.Value = vbNullString Then
MsgBox "A PDF cannot be created because no Part # selected.", , "No Part# selected."
Exit Sub
End If
With Sheet3
.Range("A28:O10000").Clear
.Range("D2:D10").Value = ""
.Range("J2:J8").Value = ""
.Range("B17:N17").Value = ""
.Range("B19:N19").Value = ""
.Range("B21:N21").Value = ""
.Range("D23").Value = ""
For Each img In Sheet3.Pictures: img.Delete: Next img '**********clears pictures prior to building new PDF********
ctr = 28
For x = 1 To items - 2
.Range("A15:O26").Copy .Range("A" & ctr)
ctr = ctr + 13
Next x
For y = 2 To 8
.Range("D" & y).Value = Me.ListBox1.List(0, y - 2)
.Range("J" & y).Value = Me.ListBox1.List(0, y - 2 + 7)
.Range("D10").Value = Me.ListBox1.List(0, 14)
Next y
ctr = 0
ctrx = 0
For x = 1 To items - 1
.Cells(17 + ctr, 2).Value = Me.ListBox1.List(ctrx, 15)
.Cells(17 + ctr, 4).Value = Me.ListBox1.List(ctrx, 16)
.Cells(17 + ctr, 6).Value = Me.ListBox1.List(ctrx, 17)
.Cells(17 + ctr, 8).Value = Me.ListBox1.List(ctrx, 18)
.Cells(17 + ctr, 10).Value = Me.ListBox1.List(ctrx, 19)
.Cells(17 + ctr, 12).Value = Me.ListBox1.List(ctrx, 20)
.Cells(17 + ctr, 14).Value = Me.ListBox1.List(ctrx, 21)
.Cells(19 + ctr, 2).Value = Me.ListBox1.List(ctrx, 22)
.Cells(19 + ctr, 4).Value = Me.ListBox1.List(ctrx, 23)
.Cells(19 + ctr, 6).Value = Me.ListBox1.List(ctrx, 24)
.Cells(19 + ctr, 8).Value = Me.ListBox1.List(ctrx, 25)
.Cells(19 + ctr, 10).Value = Me.ListBox1.List(ctrx, 26)
.Cells(19 + ctr, 12).Value = Me.ListBox1.List(ctrx, 27)
.Cells(21 + ctr, 2).Value = Me.ListBox1.List(ctrx, 28)
.Cells(21 + ctr, 6).Value = Me.ListBox1.List(ctrx, 29)
.Cells(21 + ctr, 8).Value = Me.ListBox1.List(ctrx, 30)
.Cells(21 + ctr, 12).Value = Me.ListBox1.List(ctrx, 31)
.Cells(21 + ctr, 14).Value = Me.ListBox1.List(ctrx, 32)
.Cells(23 + ctr, 4).Value = Me.ListBox1.List(ctrx, 33)
'**************************************inserting picture into PDF loader*****************
pFilename = Me.ListBox1.List(ctrx, 34)
If pFilename = "" Then GoTo Err:
Set img = .Pictures.Insert(pFilename)
With img
.Left = Sheet3.Cells(23 + ctr, 14).Left
.Top = Sheet3.Cells(23 + ctr, 14).Top
.Width = 16
.Height = 44.25
.Placement = 1
.PrintObject = True
End With
Err:
'***************************************************************************************
ctrx = ctrx + 1
ctr = ctr + 13
Next x
End With
user = Environ("Username")
FilePath = "C:\Users\" & user & "\Desktop\"
ThisWorkbook.Worksheets("Sheet3").Select
part = Sheet3.Cells(2, 4)
Application.ScreenUpdating = True
'*****************set print area and orientaton********************
Application.PrintCommunication = False
With ActiveSheet.PageSetup
.Orientation = xlLandscape
.PrintArea = Sheet3.Range(Sheet3.Cells(1, 1), Sheet3.Cells(ctr, 15))
.Zoom = False
.FitToPagesTall = False
.FitToPagesWide = 1
INSERTBREAK
End With
Application.PrintCommunication = True
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=FilePath & "\Part# " & part, OpenAfterPublish:=False, IgnorePrintAreas:=False
'*********************************************************************
Application.ScreenUpdating = True
MsgBox "Data has been exported to PDF on Desktop."
With Sheet3
.Range("A28:O10000").Clear
.Range("D2:D10").Value = ""
.Range("J2:J8").Value = ""
.Range("B17:N17").Value = ""
.Range("B19:N19").Value = ""
.Range("B21:N21").Value = ""
.Range("D23").Value = ""
For Each img In Sheet3.Pictures: img.Delete: Next img '**********clears pictures ready to build new PDF********
End With
End Sub
'********************************open file dialog box to get picture location******************
Private Sub cmdGETPHOTO_Click()
On Error Resume Next
pFilename = Application.GetOpenFilename(FileFilter:="Jpg Files (*.jpg), *.jpg", Title:="SELECT TOOL PHOTO")
Me.Image1.Picture = LoadPicture(pFilename)
Me.TextBox35.Text = pFilename
End Sub
Private Sub CommandButton1_Click()
Unload Me
End Sub
Private Sub Frame1_Click()
End Sub
Private Sub Frame2_Click()
End Sub
Private Sub ListBox1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
If Me.ListBox1.ListIndex = -1 Then Exit Sub
If Me.ListBox1.ListIndex > -1 Then sc = Me.ListBox1.ListIndex
With Me.ListBox1
For x = 1 To 35
Me.Controls("TextBox" & x).Value = .List(sc, x - 1)
Next x
tbl1row = .List(sc, 35)
End With
On Error GoTo Err
Me.Image1.Picture = LoadPicture(Me.TextBox35.Text) 'retrieves picture file location************
Exit Sub
Err: Me.Image1.Picture = LoadPicture("")
End Sub
Private Sub cmdDELETE_Click()
If Me.ListBox1.ListIndex < 0 Then
MsgBox "No Record selected", , "Errors"
Exit Sub
End If
msgValue = MsgBox("ARE YOU CERTAIN YOU WISH TO REMOVE RECORD?", vbCritical + vbYesNo + vbDefaultButton2, "Remove Record")
If msgValue = vbNo Then
CLEARFORM
Exit Sub
End If
tbl1.ListRows(tbl1row).Delete
CLEARFORM
LOADLIST
LOADCOMBO1
MsgBox ("RECORD REMOVED"), vbOKOnly + vbInformation, "Record Removed"
End Sub
Private Sub cmdUPDATE_Click()
If Me.ListBox1.ListIndex < 0 Then
MsgBox "No Record selected", , "Errors"
Exit Sub
End If
With tbl1
For x = 1 To 35
.Range(tbl1row + 1, x) = Me.Controls("TextBox" & x).Text '*********textbox35 contains picture filepath **********
Next x
End With
CLEARFORM
LOADLIST
LOADCOMBO1
End Sub
Private Sub ComboBox1_Change()
With tbl1
r = .ListRows.Count
For t = r To 1 Step -1
.DataBodyRange.Cells(t, 36) = t '******loads range with rowctr prior to spliting with 'FILTER' ***********
Next t
End With
FILTER
items = Me.ListBox1.ListCount
End Sub
Sub FILTER()
ary = tbl1.DataBodyRange
rctr = 1
For j = 1 To UBound(ary)
If ary(j, 1) = Me.ComboBox1.Text Then
rctr = rctr + 1
End If
Next j
ReDim aryx(1 To rctr, 1 To 36)
ctr = 1
For j = 1 To UBound(ary)
If ary(j, 1) = Me.ComboBox1.Text Then
For x = 1 To 36
aryx(ctr, x) = ary(j, x)
Next x
ctr = ctr + 1
End If
Next j
Me.ListBox1.List = aryx
End Sub
Sub LOADCOMBO1()
Set ws2 = Sheet2
Set tbl1 = ws2.ListObjects("Table1")
With tbl1
If .DataBodyRange.Cells(1, 1) = vbNullString Then Exit Sub
ary = .DataBodyRange
End With
Me.ComboBox1.Clear
With CreateObject("Scripting.Dictionary")
For Each rCell In tbl1.ListColumns(1).DataBodyRange
If Not .Exists(rCell.Value) And rCell.Value <> vbNullString Then .Add rCell.Value, Nothing
Next rCell
Me.ComboBox1.List = .keys
.RemoveAll
End With
End Sub
Private Sub UserForm_Initialize()
Set ws2 = Sheet2
Set tbl1 = ws2.ListObjects("Table1")
Me.ListBox1.ColumnCount = 36
s = ""
For x = 1 To 36
s = s & 50 & ";"
Next x
Me.ListBox1.ColumnWidths = s
For x = 1 To 35
Me.Controls("Label" & x).Caption = tbl1.HeaderRowRange(x)
Next x
Me.cmdADD.Enabled = True
Me.TextBox35.Enabled = False
LOADLIST
LOADCOMBO1
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
'************ensures workbook is saved if accidently closed**************
If CloseMode = vbFormControlMenu Then
Cancel = False
ThisWorkbook.Save
Unload Me
End If
End Sub
I have tried using various forms of the PageBreak method. I have also tried setting the page layout in various ways, but nothing has worked. I even tried grouping the cells in the "Item" section. I am at a total loss.

How to apply same code to new worksheets?

I want to duplicate the code below so it applies to every new sheet.
I have to manually change the code to the new sheet's name. I found loops but that coding didn't work for me. I am trying to create a stop watch function for billing hours.
I can copy sheets and create a copy, but then none of the functions work in the new sheet it says
Run-Time Error 1004: select method of Range class failed
and stops on this line:
Sheets("Client").Range("B" & iRow).Select
Sub Intialize()
Dim iRow As Long
iRow = Sheets("Client").Range("F" & Application.Rows.Count).End(xlUp).Row
'Code to Validate
If Sheets("Client").Range("D" & iRow).Value = "" Then
Sheets("Client").Range("A" & iRow).Value = Format([Today()], "DD-MMM-YYYY")
End If
End Sub
Sub Start_Time()
Dim iRow As Long
iRow = Sheets("Client").Range("F" & Application.Rows.Count).End(xlUp).Row + 1
'Code to Validate
If Sheets("Client").Range("B" & iRow).Value = "" Then
MsgBox "Please select the Task Name from the drop down.", vbOKOnly + vbInformation, "Task Name Blank"
Sheets("Client").Range("B" & iRow).Select
Exit Sub
ElseIf Sheets("Client").Range("D" & iRow).Value <> "" Then
MsgBox "Start Time is aleady captured for the selected Task."
Exit Sub
Else
Sheets("Client").Range("D" & iRow).Value = [Now()]
Sheets("Client").Range("D" & iRow).NumberFormat = "hh:mm:ss AM/PM"
End If
End Sub
Sub End_Time()
Dim iRow As Long
iRow = Sheets("Client").Range("F" & Application.Rows.Count).End(xlUp).Row + 1
'Code to Validate
If Sheets("Client").Range("D" & iRow).Value = "" Then
MsgBox "Start Time has not been captured for this task."
Exit Sub
Else
Sheets("Client").Range("E" & iRow).Value = [Now()]
Sheets("Client").Range("E" & iRow).NumberFormat = "hh:mm:ss AM/PM"
Sheets("Client").Range("F" & iRow).Value = Sheets("Client").Range("E" & iRow).Value - Sheets("Client").Range("D" & iRow).Value
Sheets("Client").Range("F" & iRow).NumberFormat = "hh:mm:ss"
End If
Call Intialize
End Sub
Well, since you said:
1 Need to copy a single Sheet
2 Apply a code to that sheet, whatever the name
3 You will create (copy) several sheet.
Here is my code.
(Paste everything in a normal module)
Option Explicit
Const A = 1
Const B = 2
Const D = 4
Const E = 5
Const F = 6
Const L = 1048576 'Excel.Application.Rows.Count
'With this you can check if you can copy the sheet
'and also, return that sheet you already checked,
'no matter the name of that sheet.
Private Function SetSheet(sht As Worksheet) As Worksheet
'This function validate if the sheet is one that you need to copy
'Assuming the first sheets of the book are used to:
'
'Parameters 1
'Main 2
'Other... 3
If sht.Index >= 4 Then 'Here is where (Sheet #4 and so on) begins...
Set SetSheet = sht
Else
'Message or do nothing...
End
End If
End Function
Sub Intialize()
' You can uncomment this DIM vars but
' need to comment the const above.
' Dim F: F = Range("F1").Column
' Dim D: D = Range("D1").Column
' Dim A: A = Range("A1").Column
' Dim L: L = Application.Rows.Count
Dim ActSht As Worksheet: Set ActSht = SetSheet(ActiveSheet)
Dim iRow As Long
iRow = ActSht.Range(Cells(L, F), Cells(L, F)).End(xlUp).Row
' Code to Validate
If ActSht.Range(Cells(iRow, D), Cells(iRow, D)).Value = "" Then
ActSht.Range(Cells(iRow, A), Cells(iRow, A)).Value = Format([Today()], "DD-MMM-YYYY")
End If
End Sub
Sub Start_Time()
' Dim B: B = Range("B1").Column
' Dim F: F = Range("F1").Column
' Dim D: D = Range("D1").Column
' Dim L: L = Application.Rows.Count
Dim ActSht As Worksheet: Set ActSht = SetSheet(ActiveSheet)
Dim iRow As Long
iRow = ActSht.Range(Cells(L, F), Cells(L, F)).End(xlUp).Row + 1
' Code to Validate
If ActSht.Range(Cells(iRow, B), Cells(iRow, B)).Value = "" Then
MsgBox "Please select the Task Name from the drop down.", vbOKOnly + vbInformation, "Task Name Blank"
ActSht.Range(Cells(iRow, B), Cells(iRow, B)).Select
Exit Sub
ElseIf ActSht.Range(Cells(iRow, D), Cells(iRow, D)).Value <> "" Then
MsgBox "Start Time is aleady captured for the selected Task."
Exit Sub
Else
ActSht.Range(Cells(iRow, D), Cells(iRow, D)).Value = [Now()]
ActSht.Range(Cells(iRow, D), Cells(iRow, D)).NumberFormat = "hh:mm:ss AM/PM"
End If
End Sub
Sub End_Time()
' Dim D: D = Range("D1").Column
' Dim F: F = Range("F1").Column
' Dim E: E = Range("E1").Column
' Dim L: L = Application.Rows.Count
Dim ActSht As Worksheet: Set ActSht = SetSheet(ActiveSheet)
Dim iRow As Long
iRow = ActSht.Range(Cells(L, F), Cells(L, F)).End(xlUp).Row + 1
' Code to Validate
If ActSht.Range(Cells(iRow, D), Cells(iRow, D)).Value = "" Then
MsgBox "Start Time has not been captured for this task."
Exit Sub
Else
ActSht.Range(Cells(iRow, E), Cells(iRow, E)).Value = [Now()]
ActSht.Range(Cells(iRow, E), Cells(iRow, E)).NumberFormat = "hh:mm:ss AM/PM"
ActSht.Range(Cells(iRow, F), Cells(iRow, F)).Value = Sheets("Client").Range("E" & iRow).Value - Sheets("Client").Range("D" & iRow).Value
ActSht.Range(Cells(iRow, F), Cells(iRow, F)).NumberFormat = "hh:mm:ss"
End If
Call Intialize
End Sub
Asummtions:
1 You call one or several of this sub-rutines from the original worksheet.
Note:
It is better not to use hardcoded this way Sheets("Client").Range("D" & iRow).Value because when you need to debug... It hurts! That is why I do prefer ActSht.Range(Cells(iRow, D), Cells(iRow, D)).Value and you can control a single var above all the code.

Error when calling sub Run-Time Error '424': Object Required

BIG PICTURE
Go through a list and create a tab for each item in the list (Working)
Create a hyperlink in the list that links to the associated worksheet (Working)
Create basic header information on each worksheet and hyperlink back to index sheet (Working)
Insert a button for each reference listed in a corresponding cell in the index sheet and hyperlink to that pdf, doc, or docx file (Not working, work in progress)
CURRENT PROBLEM
When calling the sub that will insert buttons I am getting an Object Required error (see image at end).
The main part of the code is as follows:
Sub CreateTabs()
Dim ws As Worksheet
Dim NameArray As Variant
Dim LastRow As Long
Dim x As Long
Dim y As Long
Dim z As Long
Dim ReferenceCount As Long
Dim RefSplit() As Variant
LastRow = FindLastRow
Set ws = ThisWorkbook.Sheets(1)
NameArray = ws.Range(ws.Cells(2, 1), ws.Cells(LastRow, 1)).Value
For x = LBound(NameArray) To UBound(NameArray)
ThisWorkbook.Sheets.Add(after:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)).Name = NameArray(x, 1)
'ws.Hyperlinks.Add ws.Cells(x + 1, 1), "", ThisWorkbook.Sheets(NameArray(x, 1)).Cells(1, 1).Address(External:=True), NameArray(x, 1), NameArray(x, 1)
With ThisWorkbook.Sheets(NameArray(x, 1))
ws.Hyperlinks.Add ws.Cells(x + 1, 1), "", .Cells(1, 1).Address(External:=True), .Name, .Name
.Hyperlinks.Add .Cells(1, 1), "", ws.Cells(1, 1).Address(External:=True), "Item List", "ITEM LIST"
.Cells(2, 1) = "Item"
.Cells(3, 1) = "Description"
.Cells(4, 1) = "U.O.M."
.Cells(6, 1) = "Specifications"
.Cells(2, 2).Formula = "=RIGHT(CELL(""filename"",$B$2),LEN(CELL(""filename"",$B$2))-FIND(""]"",CELL(""filename"",$B$2)))"
.Cells(3, 2).Formula = "=VLOOKUP($B$2,Sheet1!$A$2:$D$" & LastRow & ",2,0)"
.Cells(4, 2).Formula = "=VLOOKUP($B$2,Sheet1!$A$2:$D$" & LastRow & ",4,0)"
ReferenceCount = Num_Characters_In_String(ws.Cells(x + 1, 3).Value, ", ") + 1
ReDim RefSplit(1 To ReferenceCount,1)
If ReferenceCount > 1 Then
RefSplit = ReferenceSplit(ws.Cells(x + 1, 3).Value)
Else
RefSplit(1,1) = ws.Cells(x + 1, 3).Value
End If
z = 1
For y = 1 To ReferenceCount
If y > z * 5 Then z = z + 1
'*************************************************************
Call Insertbutton(z, y - (z - 1) * 5, RefSplit(y, 1).Value, ThisWorkbook.Sheets(NameArray(x, 1)))
'*************************************************************
Next y
End With
Next x
End Sub
And the sub that is being called looks as follows for now:
Sub Insertbutton(btnrow As Long, btncol As Long, btnName As String, ws As Worksheet)
Dim btn As Button
Dim rng As Range
Application.ScreenUpdating = False
ws.Buttons.Delete 'probably do not need as it is fresh sheet
Set rng = ws.Cells(btnrow + 6, btncol + 1)
Set btn = ws.Buttons.Add(rng.Left, rng.Top, rng.Width, rng.Height)
With btn
If Left(btnName, 1) = "F" Then
If Num_Characters_In_String(btnName, "-") = 2 Then
.OnAction = "P:\2019\1234-name space\08. Working\Specifications\Section F" & btnName & "*.doc*"
Else
.OnAction = "P:\2019\1234-name space\10. Construction\01. Tender\F\" & btnName & ".pdf"
End If
Else
.OnAction = "P:\2019\1234-name space\10. Construction\01. Tender\OPSS\OPSS*" & btnName & "*.pdf"
End If
.Caption = btnName
.Name = btnName
End With
Application.ScreenUpdating = True
End Sub
QUESTION
What is the missing object? What am I doing wrong with the call?
(I foresee some issues with linking to the files but I have not got to that point in my debugging yet, and that will be a different question. Trying not to muddy the waters so to speak)
I did read this question so I believe the format of the call ( ) is correct, but I could be wrong
RefSplit(y, 1).Value causes an error. RefSplit(y, 1) is correct.
Do not use .value for arrays. Because it is used for range objects, an object error occurs.
Call Insertbutton(z, y - (z - 1) * 5, RefSplit(y, 1).Value, ThisWorkbook.Sheets(NameArray(x, 1)))
However, there is another error, and the type of the argument cannot be matched. String variables should be used.
Dim myString As String
myString = RefSplit(y, 1)
Call Insertbutton(Z, y - (Z - 1) * 5, myString, ThisWorkbook.Sheets(NameArray(x, 1)))

Efficient way to match/merge several ranges or arrays by date

i am not be able to make this computation efficiently with excel (vba):
Input
Output
Doing this with tables is incredible slow, when you have a few rows and columns is working perfectly, but is no practical when you increase the number of series and rows.
What i do is update Output Table with VBA, steps:
Delete data of Output Listobject Table
Resize Listobject Range with number of dates between (min max Dates1, Dates,2)
Generate Dates and dump it in the Output Listobject Table Dates column.
I get the matching with this formula array formula in each Result row in the output listobject table:
=SUM(IF((DAY(T_1[Date])=DAY([#Date]))*(MONTH(T_1[Date])=MONTH([#Date]))*(YEAR(T_1[Date])=AÑO([#Date]));T_1[Result1]))
The Number of Series is dinamic and rows will be dynamic, i have up to 30 columns and 5000 rows. Could you give me some example or approach to achieve this more efficiently?
Here is a table with time execution speed of participants snippets. Tested with the whole data. 3161 rows x 40 columns (20 Results columns to match):
Execution time table
The following does not list the dates in order, but collects data for each input date. It is similar to the pivot table.
Compare the execution speed with other code.
Sub MergeData()
Dim strU As String
Dim myWs As Worksheet, Ws As Worksheet
Dim vTable() As Variant
Dim vFid1(), vFid2()
Dim k As Integer, n As Integer, c As Integer
Dim sWsName As String, s As String
Dim strSQL As String
Set myWs = Sheets(1) '<~~ Your data Sheet
Set Ws = Sheets(2) '<~~ Result Sheet
sWsName = myWs.Name & "$"
With myWs
c = .Cells(1, Columns.Count).End(xlToLeft).Column
For i = 1 To c Step 2
n = n + 1
ReDim Preserve vTable(1 To n)
ReDim Preserve vFid1(1 To n)
ReDim Preserve vFid2(1 To n)
vTable(n) = sWsName & .Cells(1, i).Resize(65536, 2).Address(0, 0)
vFid1(n) = "[" & .Cells(1, i) & "]"
vFid2(n) = "[" & .Cells(1, i + 1) & "]"
Next i
End With
For k = 1 To n - 1
s = Replace(vFid2(k), "[", "")
s = Replace(s, "]", "")
strU = strU & "SELECT " & vFid1(k) & " as Dates ," & vFid2(k) & " as Result , '" & s & "' as myPivot " & " FROM [" & vTable(k) & "] where not isnull(" & vFid1(k) & ") union All "
Next k
s = Replace(vFid2(n), "[", "")
s = Replace(s, "]", "")
strU = strU & "SELECT " & vFid1(n) & " as Dates," & vFid2(n) & " as Result, '" & s & "' as myPivot " & " FROM [" & vTable(n) & "] where not isnull(" & vFid1(n) & ") "
strSQL = "TRANSFORM MAX(Result) "
strSQL = strSQL & "SELECT Dates FROM "
strSQL = strSQL & "(" & strU & ") "
strSQL = strSQL & "GROUP BY Dates "
strSQL = strSQL & "ORDER BY Dates "
strSQL = strSQL & "PIVOT myPivot "
exeSQL Ws, strSQL
Ws.Range("a1").CurrentRegion.SpecialCells(xlCellTypeBlanks).Value = 0
End Sub
Sub exeSQL(Ws As Worksheet, strSQL As String)
Dim Rs As Object
Dim strConn As String
Dim i As Integer
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.FullName & ";" & _
"Extended Properties=Excel 12.0;"
Set Rs = CreateObject("ADODB.Recordset")
Rs.Open strSQL, strConn
If Not Rs.EOF Then
With Ws
.Range("a1").CurrentRegion.ClearContents
For i = 0 To Rs.Fields.Count - 1
.Cells(1, i + 1).Value = Rs.Fields(i).Name
Next
.Range("a" & 2).CopyFromRecordset Rs
End With
End If
Rs.Close
Set Rs = Nothing
End Sub
Data image
you can extend over 30 series. This is only 5 series.
Result image
First, I have made the assumption that you have dates and results in adjacent columns in the form of Dates N | Results N, image below.
Second, I have written the below code which should solve your problem. Note: this is not completely scalable as is, but you can use this now to progress further and modify to your needs. Also, please excuse my poor maths to calculate the out_col_num variable.
Option Explicit
Sub Merge_Dates()
'variables to set up dates
Dim lYear As Long: lYear = 2020
Dim lMonth As Long: lMonth = 3
Dim lDay As Long
'arrays
Dim arr_in() As Variant
Dim arr_out() As Variant
Dim x_in As Long, y_in As Long
Dim x_out As Long, y_out As Long
Dim out_col_num As Long, n As Long: n = 1
arr_in = ActiveSheet.UsedRange.Value
'we need to define the bounds for the output array
'this will contain all dates for March (in this example)
'also hold the results in the columns - this is a function on lbound(2)
ReDim arr_out(1 To 32, 1 To (UBound(arr_in, 2) - 1))
'header for out array
arr_out(1, 1) = "Dates"
'load dates
For lDay = 1 To 31
arr_out(lDay + 1, 1) = CDate(Format(DateSerial(lYear, lMonth, lDay), "DD/MM/YYYY"))
Next lDay
'set column headers
For x_out = LBound(arr_out, 2) + 1 To UBound(arr_out, 2)
arr_out(1, x_out) = "Results" & (x_out - 1)
Next x_out
'now loop through in array and map to out array
'you can do this multiple ways, below is just one
'loop x dim in array
For x_in = LBound(arr_in, 2) To UBound(arr_in, 2) Step 2
'loop y dim in array
For y_in = LBound(arr_in, 1) + 1 To UBound(arr_in, 1)
'loop y dim out array to store result
For y_out = LBound(arr_out, 1) + 1 To UBound(arr_out, 1)
If arr_out(y_out, 1) = arr_in(y_in, x_in) Then
'out column is a function of in column
'-n + 3n
out_col_num = (-1 * x_in) + (3 * n)
arr_out(y_out, out_col_num) = arr_in(y_in, x_in + 1)
Exit For
End If
Next y_out
Next y_in
'increment n
n = n + 1
Next x_in
'output
ActiveSheet.Range("A10").Resize(UBound(arr_out, 1), UBound(arr_out, 2)).Value = arr_out
End Sub
So given the example, assuming your dates cover only March 2020 (something you will have to modify to build it more scalable):
Will give the output as below:
I've put something together using ADODB Recordsets, so that I can use .Filter and .Find. This code outputs the unique dates, and then the result on that date for each result set.
Const AD_DATE = 7
Const AD_VARIANT = 12
Const AD_BIGINT = 20
Const AD_VARCHAR = 200
Const AD_FILTERNONE = 0
Sub sResultData()
On Error GoTo E_Handle
Dim aResultSet() As String
Dim lngMaxCol As Long
Dim lngMaxRow As Long
Dim lngLoopRow As Long
Dim lngLoopCol As Long
Dim rsMaster As Object
Dim rsDate As Object
Set rsMaster = CreateObject("ADODB.Recordset")
Set rsDate = CreateObject("ADODB.Recordset")
lngMaxRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
lngMaxCol = ActiveSheet.Cells(1, ActiveSheet.Columns.Count).End(xlToLeft).Column
With rsMaster.Fields
.Append "ResultDate", AD_DATE
.Append "ResultSet", AD_VARCHAR, 50
.Append "ResultData", AD_BIGINT
End With
With rsDate.Fields
.Append "ResultDate", AD_DATE
End With
rsMaster.Open
rsDate.Open
ReDim aResultSet(1 To lngMaxCol / 2)
For lngLoopCol = 2 To lngMaxCol Step 2
aResultSet(lngLoopCol / 2) = ActiveSheet.Cells(1, lngLoopCol)
Next lngLoopCol
For lngLoopRow = 2 To lngMaxRow
For lngLoopCol = 2 To lngMaxCol Step 2
With rsMaster
.AddNew
!ResultDate = ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1)
!ResultSet = ActiveSheet.Cells(1, lngLoopCol)
!ResultData = ActiveSheet.Cells(lngLoopRow, lngLoopCol)
.Update
End With
If (rsDate.BOF And rsDate.EOF) Then ' dealing with first record, so cannot do .Find
rsDate.AddNew
rsDate!ResultDate = ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1)
rsDate.Update
Else
rsDate.MoveFirst
rsDate.Find "ResultDate=" & Format(ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1), "dd/mmm/yyyy")
If (rsDate.EOF) Or (rsDate.EOF) Then
rsDate.AddNew
rsDate!ResultDate = ActiveSheet.Cells(lngLoopRow, lngLoopCol - 1)
rsDate.Update
End If
End If
Next lngLoopCol
Next lngLoopRow
rsDate.Sort = "ResultDate ASC"
rsDate.MoveFirst
rsMaster.Sort = "ResultSet ASC, ResultDate ASC"
For lngLoopCol = 1 To UBound(aResultSet)
lngLoopRow = lngMaxRow + 5
ActiveSheet.Cells(lngLoopRow - 1, lngLoopCol + 1) = aResultSet(lngLoopCol)
rsMaster.Filter = AD_FILTERNONE
rsMaster.Filter = "ResultSet='" & aResultSet(lngLoopCol) & "'"
rsDate.MoveFirst
Do
ActiveSheet.Cells(lngLoopRow, 1) = rsDate!ResultDate
rsMaster.MoveFirst
rsMaster.Find "ResultDate=#" & Format(rsDate!ResultDate, "dd-mmm-yy") & "#"
If Not rsMaster.EOF Then
ActiveSheet.Cells(lngLoopRow, lngLoopCol + 1) = rsMaster!ResultData
End If
lngLoopRow = lngLoopRow + 1
rsDate.MoveNext
Loop Until rsDate.EOF
Next lngLoopCol
sExit:
On Error Resume Next
rsDate.Close
rsMaster.Close
Set rsDate = Nothing
Set rsMaster = Nothing
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sResultData", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
Regards,
Test the next code, please. It will deal with as many (pairs of) columns you will have. It determines the minimum, respectively, maximum used date and iterates between the determined interval, collecting data in arrFin array. You can also use any Date interval. The interval will be automatically determined. My code drops the values one column after the existing range. This is done only for testing reasons. I have to test it in a way... You can drop them wherever you need. So, if you intend to run the code for the second time, you must delete the previously returned values.
Sub testMatchReArrange()
Dim sh As Worksheet, arrD As Variant, DateRng As Range, lastCol As Long, lastRow As Long
Dim i As Long, dateStart As Date, dateFinish As Date, dDiff As Long, arrFin As Variant
Dim boolFound As Boolean, checkDate As Date, j As Long, k As Long, f As Long
Set sh = ActiveSheet 'use here your sheet
lastRow = sh.Range("A" & Rows.count).End(xlUp).Row
lastCol = sh.Cells(1, Columns.count).End(xlToLeft).Column
arrD = sh.Range(sh.Cells(2, 1), Cells(lastRow, lastCol)).value 'array to be processed
'create the specific range keeping only Date, in order to determine the correct date interval. Especially the minimum date...
For i = 1 To lastCol Step 2
If DateRng Is Nothing Then
Set DateRng = sh.Range(sh.Cells(2, i), sh.Cells(lastRow, i))
Else
Set DateRng = Union(DateRng, sh.Range(sh.Cells(2, i), sh.Cells(lastRow, i)))
End If
Next i
dateStart = WorksheetFunction.Min(DateRng) 'starting date
dateFinish = WorksheetFunction.Max(DateRng) 'finishing date
dDiff = dateFinish - dateStart 'the date interval to be processed
'Properly dimension the array to collect the processing result:
ReDim arrFin(1 To dDiff + 2, 1 To lastCol / 2 + 1): f = 1
'Load the head of columns:
arrFin(1, 1) = "Dates"
For i = 2 To lastCol / 2 + 1
arrFin(1, i) = "result" & i - 1
Next i
f = 2 're-initializing the row of for real processed data
checkDate = dateStart 'initialize the date to be used for processing
For i = 1 To dDiff + 1 'for each date in the processed date interval
For j = 1 To UBound(arrD, 1) 'for each row in the processed array
For k = 1 To UBound(arrD, 2) Step 2 'for each column in the processed array (but looking only in add columns)
If CDate(arrD(j, k)) = checkDate Then
arrFin(f, 1) = checkDate: arrFin(f, (k + 1) / 2 + 1) = arrD(j, k + 1)
boolFound = True 'confirming that at least a match exist
End If
Next k
Next j
If Not boolFound Then arrFin(f, 1) = checkDate' Record the date in case of no any match
boolFound = False: f = f + 1
checkDate = checkDate + 1
Next i
'you can use here any other location (sheet, range) to drop the resulted array:
sh.Cells(1, lastCol + 2).Resize(UBound(arrFin, 1), UBound(arrFin, 2)).value = arrFin
End Sub
In case of a big range, it needs some time, but working only in memory (using arrays) it is the maximum possible speed for such a task.
The code below allows you to specify the columns from which to collect the data and outputs the result on a dedicated sheet which would need to be inserted for that purpose. I called it "Output" but you can pick a name that suits you better. Your original data will not be touched.
Sub MergeDataByDate()
' 006
' define the origin of your data
Const FirstDataRow As Long = 2 ' applicable to both data sets
' set the columns to what they are on your sheet (A = 1, B = 2 etc)
Const C1 As Long = 2 ' Date 1 column
Const Cr1 As Long = 3 ' Result 1 column
Const C2 As Long = 8 ' Date 2 column
Const Cr2 As Long = 11 ' Result 2 column
Dim WsOut As Worksheet ' worksheet for output
Dim ArrIn As Variant ' for input
Dim Arr() As Variant ' for output
Dim Dat As Date ' date counter
Dim Rng As Range
Dim i As Long ' Arr index
Dim R As Long ' row counter
Set WsOut = Worksheets("Output") ' the output sheet must exist: rename to suit
With Worksheets("Input") ' use your tab's name
Set Rng = .Range(.Cells(FirstDataRow, 1), _
.Cells(.Rows.Count, C1).End(xlUp) _
.Offset(0, Cr2 - C1))
ArrIn = Rng.Value
ReDim Arr(1 To 3, (2 * UBound(ArrIn)))
For R = 1 To UBound(ArrIn)
Arr(1, i) = ArrIn(R, C1)
Arr(2, i) = ArrIn(R, Cr1)
Arr(1, i + 1) = ArrIn(R, C2)
Arr(3, i + 1) = ArrIn(R, Cr2)
i = i + 2
Next R
End With
Application.ScreenUpdating = False
With WsOut
Set Rng = .Cells(2, 1).Resize(UBound(Arr, 2), UBound(Arr))
Rng.Value = Application.Transpose(Arr)
With .Sort
With .SortFields
.Clear
.Add Key:=Rng.Cells(1), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
End With
.SetRange Rng
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End With
Arr = Rng.Value
For R = (UBound(Arr) - 1) To 1 Step -1
If Arr(R + 1, 1) = Arr(R, 1) Then
Arr(R, 2) = Arr(R, 2) + Arr(R + 1, 2)
Arr(R, 3) = Arr(R, 3) + Arr(R + 1, 3)
For i = 1 To 3
Arr(R + 1, i) = vbNullString
Next i
Else
Arr(R, 2) = Val(Arr(R, 2)) + 0
Arr(R, 3) = Val(Arr(R, 3)) + 0
End If
Next R
Rng.Value = Arr
With WsOut ' sort blanks to the bottom
With .Sort
With .SortFields
.Clear
.Add Key:=Rng.Cells(1), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
End With
.SetRange Rng
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
R = .Cells(.Rows.Count, 1).End(xlUp).Row
Dat = CLng(Cells(R, 1).Value)
For R = R To 3 Step -1
Dat = Dat - 1
Do Until .Cells(R - 1, 1).Value = Dat
.Rows(R).Insert
.Cells(R, 1).Value = Dat
.Cells(R, 2).Value = 0
.Cells(R, 3).Value = 0
Dat = Dat - 1
Loop
Next R
End With
Application.ScreenUpdating = True
End Sub
The code first combines the existing data to a single list, then sorts the list by date. It then unites data from the same days into single lines, deleting the lines that become redundant and sort them to the end of the list where they disappear.
In the last step the remaining data are checked for dates and missing dates are inserted between the last first date in the list and the last. If you are particular about those dates, for example, you want them to be the first and last days of a month it's the most efficient to add those two days with zero results anywhere in the original data. If results for those dates exist the zero values will be discarded. If they don't they, and any intervening days, will be added to the output.
Took me a while, but here is my code:
Sub SubOutput()
'Declarations.
Dim WksInput As Worksheet
Dim WksOutput As Worksheet
Dim RngInputFirstCell As Range
Dim RngOutputFirstCell As Range
Dim BytOffset As Byte
Dim RngRange01 As Range
Dim RngTarget As Range
Dim BytWholeCalendar As Byte
Dim DatFirstDate As Date
Dim DatLastDate As Date
Dim IntCounter01 As Integer
'Setting variables.
Set WksInput = Sheets("Input") 'put here the name of the worksheet with input data
Set WksOutput = Sheets("Output") 'put here the name of the worksheet with the output data
Set RngInputFirstCell = WksInput.Range("A1") 'put here the top left cell of the input data (the one with value Dates1)
Set RngOutputFirstCell = WksOutput.Range("A1") 'put here the top left cell of the output data (the one with value Dates)
'Asking what days are to be reported.
BytWholeCalendar = MsgBox("Do you need the output to report data for every day?", vbYesNoCancel, "Report every day?")
'In case of no answer, the subroutine is terminated.
If BytWholeCalendar <> 6 And BytWholeCalendar <> 7 Then
Exit Sub
End If
'Typing "Dates" in RngOutputFirstCell.
RngOutputFirstCell = "Dates"
'Covering the entire input.
Do Until RngInputFirstCell.Offset(0, BytOffset * 2) = ""
'Setting first part of the range to be copied (dates).
Set RngRange01 = WksInput.Range(RngInputFirstCell.Offset(1, BytOffset * 2), WksInput.Cells(WksInput.Rows.Count, RngInputFirstCell.column + BytOffset * 2).End(xlUp))
'Setting the range where to paste the dates.
Set RngTarget = WksOutput.Cells(WksOutput.Rows.Count, RngOutputFirstCell.column).End(xlUp).Offset(1, 0)
Set RngTarget = RngTarget.Resize(RngRange01.Rows.Count)
'Pasting the dates.
RngTarget.Value = RngRange01.Value
'Copying the result name.
RngOutputFirstCell.Offset(0, BytOffset + 1).Value = RngInputFirstCell.Offset(0, BytOffset * 2 + 1).Value
'Setting BytOffset to cover the next rows of data.
BytOffset = BytOffset + 1
Loop
'Editing the dates according to BytWholeCalendar.
Select Case BytWholeCalendar
Case Is = 6
'Setting variables.
DatFirstDate = Excel.WorksheetFunction.Min(WksOutput.Range(RngOutputFirstCell.Offset(1, 0), RngOutputFirstCell.End(xlDown)))
DatLastDate = Excel.WorksheetFunction.Max(WksOutput.Range(RngOutputFirstCell.Offset(1, 0), RngOutputFirstCell.End(xlDown)))
IntCounter01 = 1
'Clearing dates.
WksOutput.Range(RngOutputFirstCell.Offset(1, 0), RngOutputFirstCell.End(xlDown)).ClearContents
'Filling dates.
For DatFirstDate = DatFirstDate To DatLastDate
RngOutputFirstCell.Offset(IntCounter01, 0).Value = DatFirstDate
IntCounter01 = IntCounter01 + 1
Next DatFirstDate
Case Is = 7
'Sorting output dates.
With WksOutput.Sort
.SortFields.Clear
.SortFields.Add Key:=RngOutputFirstCell, _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
.SetRange Range(RngOutputFirstCell, RngOutputFirstCell.End(xlDown))
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'Marking unique dates.
Set RngTarget = WksOutput.Range(RngOutputFirstCell.Offset(1, 1), RngOutputFirstCell.End(xlDown).Offset(0, 1))
RngTarget.FormulaR1C1 = "=IF(RC[-1]=R[1]C[-1],"""",""X"")"
RngTarget.Value = RngTarget.Value
'Sorting output dates by unique values.
With WksOutput.Sort
.SortFields.Clear
.SortFields.Add Key:=RngOutputFirstCell.Offset(0, 1), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortTextAsNumbers
.SetRange Range(RngOutputFirstCell.Offset, RngOutputFirstCell.End(xlDown).Offset(0, 1))
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'Clearing double dates.
With WksOutput.Range(RngOutputFirstCell.End(xlDown), RngOutputFirstCell.Offset(0, 1).End(xlDown).Offset(1, 0))
.ClearContents
.ClearFormats
End With
End Select
'Setting RngTarget to cover the results' part of the output.
Set RngTarget = WksOutput.Range(RngOutputFirstCell.Offset(1, 1), RngOutputFirstCell.End(xlDown).Offset(0, 1))
Set RngTarget = RngTarget.Resize(, BytOffset)
RngTarget.FormulaR1C1 = "=VLOOKUP(RC" & RngOutputFirstCell.column & ",OFFSET(INDIRECT(""" & WksInput.Name & "!R" & RngInputFirstCell.Row + 1 & "C""" & " & MATCH(R" & RngOutputFirstCell.Row & "C," & WksInput.Name & "!" & WksInput.Range(RngInputFirstCell, RngInputFirstCell.End(xlToRight)).Address(, , xlR1C1) & ",0) + " & RngInputFirstCell.column - 1 & ",FALSE),0,-1,5000,2),2,FALSE)"
'Typing in RngTarget the formula.
'RngTarget.FormulaR1C1 = "=IFERROR(VLOOKUP(RC" & RngOutputFirstCell.column & ",OFFSET(INDIRECT(""" & WksInput.Name & "!R" & RngInputFirstCell.Row + 1 & "C""" & " & MATCH(R" & RngOutputFirstCell.Row & "C," & WksInput.Name & "!" & WksInput.Range(RngInputFirstCell, RngInputFirstCell.End(xlToRight)).Address(, , xlR1C1) & ",0) + " & RngInputFirstCell.column - 1 & ",FALSE),0,-1,5000,2),2,FALSE),0)"
'Transforming formulas into values.
'RngTarget.Value = RngTarget.Value
'Setting RngTarget to select the output data.
Set RngTarget = RngTarget.Offset(0, -1).Resize(, RngTarget.Columns.Count + 1)
'Formatting.
With RngTarget
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
.Borders(xlInsideHorizontal).LineStyle = xlNone
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
End With
'Setting RngTarget to select the output labels.
Set RngTarget = RngTarget.Offset(-1, 0).Resize(1)
'Formatting.
With RngTarget
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
.Borders(xlInsideHorizontal).LineStyle = xlNone
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlMedium
End With
End With
RngTarget.EntireColumn.AutoFit
Debug.Print "REPORT"; " | "
Debug.Print "WksInput.Parent.Name = WksOutput.Parent.Name ? "; WksInput.Parent.Name = WksInput.Parent.Name; " | "
Debug.Print "WksInput.Name ? "; WksInput.Name; " | "
Debug.Print "RngInputFirstCell.Address ? "; RngInputFirstCell.Address; " | "
Debug.Print "RngInputFirstCell.Value ? "; RngInputFirstCell.Value; " | "
Debug.Print "RngInputFirstCell.Formula ? "; RngInputFirstCell.Formula; " | "
Debug.Print "RngInputFirstCell.Offset(1,0).Address ? "; RngInputFirstCell.Offset(1, 0).Address; " | "
Debug.Print "RngInputFirstCell.Offset(1,0).Value ? "; RngInputFirstCell.Offset(1, 0).Value; " | "
Debug.Print "RngInputFirstCell.Offset(1,0).Formula ? "; RngInputFirstCell.Offset(1, 0).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(0,1).Address ? "; RngInputFirstCell.Offset(0, 1).Address; " | "
Debug.Print "RngInputFirstCell.Offset(0,1).Value ? "; RngInputFirstCell.Offset(0, 1).Value; " | "
Debug.Print "RngInputFirstCell.Offset(0,1).Formula ? "; RngInputFirstCell.Offset(0, 1).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(1,1).Address ? "; RngInputFirstCell.Offset(1, 1).Address; " | "
Debug.Print "RngInputFirstCell.Offset(1,1).Value ? "; RngInputFirstCell.Offset(1, 1).Value; " | "
Debug.Print "RngInputFirstCell.Offset(1,1).Formula ? "; RngInputFirstCell.Offset(1, 1).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(91,0).Address ? "; RngInputFirstCell.Offset(91, 0).Address; " | "
Debug.Print "RngInputFirstCell.Offset(91,0).Value ? "; RngInputFirstCell.Offset(91, 0).Value; " | "
Debug.Print "RngInputFirstCell.Offset(91,0).Formula ? "; RngInputFirstCell.Offset(91, 0).Formula; " | "
Debug.Print "RngInputFirstCell.Offset(91,1).Address ? "; RngInputFirstCell.Offset(91, 1).Address; " | "
Debug.Print "RngInputFirstCell.Offset(91,1).Value ? "; RngInputFirstCell.Offset(91, 1).Value; " | "
Debug.Print "RngInputFirstCell.Offset(91,1).Formula ? "; RngInputFirstCell.Offset(91, 1).Formula; " | "
Debug.Print "WksOutput.Name ? "; WksOutput.Name; " | "
Debug.Print "RngOutputFirstCell.Address ? "; RngOutputFirstCell.Address; " | "
Debug.Print "RngOutputFirstCell.Value ? "; RngOutputFirstCell.Value; " | "
Debug.Print "RngOutputFirstCell.Formula ? "; RngOutputFirstCell.Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(1,0).Address ? "; RngOutputFirstCell.Offset(1, 0).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(1,0).Value ? "; RngOutputFirstCell.Offset(1, 0).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(1,0).Formula ? "; RngOutputFirstCell.Offset(1, 0).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(0,1).Address ? "; RngOutputFirstCell.Offset(0, 1).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(0,1).Value ? "; RngOutputFirstCell.Offset(0, 1).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(0,1).Formula ? "; RngOutputFirstCell.Offset(0, 1).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(1,1).Address ? "; RngOutputFirstCell.Offset(1, 1).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(1,1).Value ? "; RngOutputFirstCell.Offset(1, 1).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(1,1).Formula ? "; RngOutputFirstCell.Offset(1, 1).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(91,0).Address ? "; RngOutputFirstCell.Offset(91, 0).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(91,0).Value ? "; RngOutputFirstCell.Offset(91, 0).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(91,0).Formula ? "; RngOutputFirstCell.Offset(91, 0).Formula; " | "
Debug.Print "RngOutputFirstCell.Offset(91,1).Address ? "; RngOutputFirstCell.Offset(91, 1).Address; " | "
Debug.Print "RngOutputFirstCell.Offset(91,1).Value ? "; RngOutputFirstCell.Offset(91, 1).Value; " | "
Debug.Print "RngOutputFirstCell.Offset(91,1).Formula ? "; RngOutputFirstCell.Offset(91, 1).Formula; " | "
End Sub
Bit long, yep. Still it should work. Just make sure to properly set those 4 variables at the beginning (WksInput, WksOutput, RngInputFirstCell, RngOutputFirstCell). Notes will guide you. The code writes on previous output but it doesn't clear it (still it can be modify accordingly). It also apply part of the format you've used in your examples (with more details it's possible to completely edit the format).
If you need any clarification, just say please.

Retrieving row values from another file

I have been working in this project step by step. I can't understand why it is not copying the row string values from the "SheetName" used as argument being passed into this function(SheetName). The function can read a file and create a second file with checkboxes based on the number of column titles found in the first file, but the column titles are not being copied into the second file as captions for the checkboxes. Any help is appreciated.
Function CallFunction(SheetName As Variant) As Long
Dim text As String
Dim titles(200) As String ' Dim titles(200) As String ' Array
Dim nTitles As Integer
Dim wks As Worksheet
Dim myCaption As String
Dim NewBook As Workbook
PathName = Range("F22").Value
Filename = Range("F23").Value
ControlFile = ActiveWorkbook.Name
Workbooks.Open Filename:=PathName & "\" & Filename
Set wks = ActiveWorkbook.Worksheets(SheetName)
For i = 1 To 199
If Trim(wks.Cells(4, i).Value) = "" Then
nTitles = i - 1
Exit For
End If
titles(i - 1) = wks.Cells(4, i).Value
Next
i = 1
Workbooks.Add
Set NewBook = ActiveWorkbook
NewBook.SaveAs fileExported
Workbooks.Open (fileExported)
For Each cell In Range(Sheets(SheetName).Cells(4, 1), Sheets(SheetName).Cells(4, 1 + nTitles))
myCaption = Sheets(SheetName).Cells(4, i).Value
With Sheets(SheetName).checkBoxes.Add(cell.Left, _
cell.Top, cell.Width, cell.Height)
.Interior.ColorIndex = 12
.Caption = myCaption
.Characters.text = myCaption
.Border.Weight = xlThin
.Name = myCaption
End With
i = i + 1
Next
End Function
I found the answer to my own question I just forgot to add the answer here. Ok, here it is
' Save all Jira column titles into jTitles
If sj = True Or ji = True Then
For j = 1 To 199
If Trim(wks1.Cells(4, j).Value) = "" Then
titlesj = j - 1
Exit For
End If
jTitles(j - 1) = wks1.Cells(4, j).Value
Next
j = 1
' Add column titles as checkboxes
For j = 0 To titlesj
Sheet1.ListBox1.AddItem jTitles(j)
Sheet1.ListBox3.AddItem jTitles(j)
Next
wb1.Close
End If

Resources