Unable to get ActiveX checkbox properties using application caller - excel

I'm trying to hide columns if their headings match a checkbox name. These ActiveX checkboxes have been created based on the column headings provided.
For iCol = colNum To totalColumns
Set colCheckbox = ActiveSheet.OLEObjects.Add(ClassType:="Forms.CheckBox.1")
With colCheckbox
.Left = Cells(iCol + 2, 1).Left
.Top = Cells(iCol + 2, 1).Top
.Width = Cells(iCol + 2, 1).Width * 0.8
.Height = Cells(iCol + 2, 1).Height * 0.8
.Name = "cbx" & iCol
.Object.Caption = Cells(RowNum, iCol).Value
End With
Next iCol
When the checkboxes are selected / deselected the code below will find the column heading and hide the column.
Private Sub CheckBox1_Click()
Set matchingAddress = Rows("4").Find(CheckBox1.Caption, LookIn:=xlFormulas)
If matchingAddress Is Nothing Then
MsgBox ("Column Not Found")
Else
cellAddressSplit = Split(matchingAddress.Address, "$")
Columns(cellAddressSplit(1)).EntireColumn.Hidden = Not CheckBox1
End If
End Sub
I'm trying to move this code to a module, so that I can just pass the checkbox name (CheckBox1.Name) to the module function and manipulate the checkbox's visibility. I'm looking at 40 plus columns on the sheet and I want the same code in all Click methods. I'm trying to replace CheckBox1 with
Set cBox = ActiveSheet.CheckBoxes(Application.Caller)
or
Set cBox = ActiveSheet.Shapes(Application.Caller)
These are not working. Please provide directions. Should I be using forms instead of ActiveX checkboxes?
A sample error I get is "Unable to get checkboxes property of the worksheet class"

Sub CreateCheckBox()
colNum = 1
totalColumns = 3
RowNum = 8
For iCol = colNum To totalColumns
Set colCheckBox = ActiveSheet.CheckBoxes.Add(305.25, 158.25, 62.25, 17.25)
With colCheckBox
.Left = Cells(iCol + 2, 1).Left
.Top = Cells(iCol + 2, 1).Top
.Width = Cells(iCol + 2, 1).Width * 0.8
.Height = Cells(iCol + 2, 1).Height * 0.8
.Name = "cbx" & iCol
.Characters.Text = Cells(RowNum, iCol).Value
.OnAction = "HideColumn"
End With
Next
End Sub
Sub HideColumn()
Set cBox = ActiveSheet.CheckBoxes(Application.Caller)
Set matchingAddress = Rows(8).Find(cBox.Caption, LookIn:=xlFormulas)
If matchingAddress Is Nothing Then
MsgBox ("Column Not Found")
Else
If Columns(matchingAddress.Column).EntireColumn.Hidden = True Then
Columns(matchingAddress.Column).EntireColumn.Hidden = False
Else
Columns(matchingAddress.Column).EntireColumn.Hidden = True
End If
End If
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.

Click TextBox Open FileDialog - On run time

I'm using a form to generate 3 groups of textboxes at run time. I'm trying to achieve this: when the user click a textbox of the group "txtboxe", a FileDialog opens so the user can choose the file. When the file is picked, i wwant to get the filepath.
The problem is, usually, i would use a sub like - textboxe_Click - but i need this to work inside a existing module, and the name of each textbox is generate at run time.
So... Any ideas? I tried to use multiple times something with 'Controls' commands, but without any sucess. I manage to pick the data from each textbox, but not to control a click or change on a textbox group.
I'm providing the code below.
Dim i As Long
Dim o As Long
Dim number As Long
number = InputBox("How many docs?", "Number of docs")
Dim txtB1 As Control
Dim txtB2 As Control
For i = 1 To number
Set txtB1 = Controls.Add("Forms.Textbox.1")
With txtB1
.Name = "txtbox" & i
.Height = 16
.Width = 30
.Left = 60
.Top = 20 + (i * 40)
.Value = i
.Locked = True
.BackColor = &H80000000
End With
Set txtB2 = Controls.Add("Forms.Textbox.1")
With txtB2
.Name = "txtboxw" & i
.Height = 18
.Width = 234
.Left = 162
.Top = 20 + (i * 40)
Debug.Print .Name
End With
Set txtB3 = Controls.Add("Forms.Textbox.1")
With txtB3
.Name = "txtboxe" & i
.Height = 18
.Width = 264
.Left = 420
.Top = 20 + (i * 40)
Debug.Print .Name
End With
Next i
End Sub
Private Sub CommandButton3_Click()
Dim p As Long
For p = 1 To number
cells(p + 1, 1) = Controls("txtbox" & p).Value
cells(p + 1, 2) = Controls("txtboxw" & p).Value
cells(p + 1, 3) = Controls("txtboxe" & p).Value
Next p
End Sub ````
Registering event handlers with dynamically created controls is tricky.
But you can do it using a Class Module and WithEvent variable.
First, add the following code as a Class Module ControlEvent.
Option Explicit
Private WithEvents targetCtrl As MSForms.TextBox
Public Sub SetCtrl(new_ctrl As MSForms.TextBox)
Set targetCtrl = new_ctrl
End Sub
' You can add arbitrary event handlers for TextBox as ``targetCtrl_(Event handler name)``
Private Sub targetCtrl_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
With Application.FileDialog(msoFileDialogFilePicker)
If .Show() Then
' You can access the control with the targetCtrl class variable
targetCtrl.Value = .SelectedItems(1)
End If
End With
End Sub
Then, modify your code in the UserForm to the following.
I added an array ctrls to store the ControlEvent class instances and set the created controls to the WithEvents class variables with SetCtrl method.
Private ctrls As Variant ' Added
Sub CommandButton2_Click()
Dim i As Long
Dim o As Long
Dim number As Long
number = InputBox("How many docs?", "Number of docs")
Dim txtB1 As Control
Dim txtB2 As Control
Dim txtB3 As Control
ReDim ctrls(0 To number - 1)
For i = 1 To number
Set txtB1 = Controls.Add("Forms.Textbox.1")
With txtB1
.Name = "txtbox" & i
.Height = 16
.Width = 30
.Left = 60
.Top = 20 + (i * 40)
.Value = i
.Locked = True
.BackColor = &H80000000
End With
Set txtB2 = Controls.Add("Forms.Textbox.1")
With txtB2
.Name = "txtboxw" & i
.Height = 18
.Width = 234
.Left = 162
.Top = 20 + (i * 40)
Debug.Print .Name
End With
Set txtB3 = Controls.Add("Forms.Textbox.1")
With txtB3
.Name = "txtboxe" & i
.Height = 18
.Width = 264
.Left = 420
.Top = 20 + (i * 40)
Debug.Print .Name
End With
Set ctrls(i - 1) = New ControlEvent ' Added
ctrls(i - 1).SetCtrl txtB3 ' Added
Next i
End Sub

How to check if optionbutton is selected for the group and store the value of the selected button for additional calculation in VBA?

This is the first time I've done something this complicated in UserForms using VBA. I'm not sure if this is even the correct way to do this or not.
The layout of the form
The form as a MultiPage with 5 pages.
Each Page has 5 survey questions with 4 option buttons per question (Low, Medium, High, Don't know)
4 option buttons per question are grouped
Low option button should have a value of 1, Medium 3, and High 5. Don't know should be 0 but if more
Then values from these option buttons are averaged for each page. e.g. Page 1 has 5 questions, the user selects 1. Low, 2. Low, 3. Medium, 4. Don't know, 5. High. Then the average for this page should be 2. And this average is the only one I care about and needs to be save in a sheet range (which I can do later). I don't need to store other responses.
Because the pages in the Multipage can change in the future, I created a loop to create the page dynamically based on items added in the table in a separate sheet.
Also, because questions can be added or removed for each page, I also created the labels for the question and radio buttons using the loop.
What I don't know how to do next
The layout is complete and works. What I need to do now is the following:
Make the option buttons mandatory, i.e. the user must select one option button per group.
Get the value of the option buttons for each group and calculate the average for each page
I don't know yet how to get the value of the option button when it's used in the loop. I just learnt that VBA is an event driven program, so now I'm concerned whether I can salvage the time and effort I put in creating the form using loop the following way.
In this case, I also tried to create a dictionary, store the values. But I need to store this in a collection?
Private Sub CreateAssessmentForm()
'Range variable for data from table
Dim rngTable As Range
Dim itemTable As Range
'UI forms and multipage
'Dim multiPage As MSForms.multiPage
'Labels
Dim itemLblBackground As Object
Dim itemLbl As Object
Dim lowRatingLabelBg As Object
Dim medRatingLabelBg As Object
Dim highRatingLabelBg As Object
Dim unknownRatingLabelBg As Object
Dim lowRatingLabel As Object
Dim medRatingLabel As Object
Dim highRatingLabel As Object
Dim unknownRatingLabel As Object
'Misc
Dim lblName As String
Dim lblBackground As String
Dim JobGrade As Integer
Dim itemTopPosition As Integer
Dim altBgClr As Long
'Leader Role table ID to keep track of for the Multipage tabs
Dim activeLeaderRoleId As String
'Radio buttons
Dim lowRadioButton As MSForms.OptionButton
Dim medRadioButton As MSForms.OptionButton
Dim highRadioButton As MSForms.OptionButton
Dim unknownRadioButton As MSForms.OptionButton
Set RadioDictionary = New Dictionary
'================================================================
Me.drName = Sheets("var").Range("Y2").Value
'Declaration
JobGrade = Sheets("var").Range("z2").Value
Me.dateLabel.Caption = DateValue(Now)
Me.ManagerView_SubmitAssessment.Enabled = True
'Define source range, referring to the table data range
Set rngTable = ThisWorkbook.Worksheets("LeaderRoles2").Range("leaderRoleTable2")
Set itemTable = ThisWorkbook.Worksheets("Items").Range("itemTable")
'Create a tabbed multipage to add the leader role information
Set multiPage = AssessmentForm.DriverTabs
multiPage.Font.Bold = False
multiPage.Font.Name = "Verdana"
multiPage.Font.Size = 9
multiPage.Style = fmSpecialEffectFlat
multiPage.ForeColor = RGB(0, 40, 85)
'Total page counts that may already exist
PageCount = multiPage.Pages.Count
Dim itemLabelWidth As Integer
itemLabelWidth = 325
'rngTable rows have different count than rngTable.Count if multiple columns are selected
For i = 1 To rngTable.Rows.Count
itemTopPosition = (multiPage.Height - (32 * 5)) / 2
'get leader role id
activeLeaderRoleId = rngTable.Item(i, 1).Value
If (i <= PageCount) = True Then
multiPage.Pages(i - 1).Caption = rngTable.Item(i, 2).Value
Else
multiPage.Pages.Add
Me.DriverTabs.Pages(i - 1).Name = "Page" & i
Me.DriverTabs.Pages(i - 1).Caption = rngTable.Item(i, 2).Value
End If
'Tab height
multiPage.TabFixedHeight = 25
lblName = "lr" & i & "Item" & j
lblBackground = "lblBkgrnd" & i & j
'Multipage page background
With multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
.BackColor = RGB(255, 255, 255)
.Width = multiPage.Width
.Height = multiPage.Height
.Top = 0
.Left = 0
End With
'For alternating the background in the item labels
Dim k As Integer
k = 1
'Loop through item table to add items to the multipage page
For j = 1 To itemTable.Rows.Count
If itemTable.Item(j, 2).Value = activeLeaderRoleId And itemTable.Item(j, 3) = JobGrade Then
Set itemLblBackground = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1", lblBackground)
Set itemLbl = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1", lblName)
altBgClr = RGB(250, 250, 250)
If (k Mod 2 = 0) Then altBgClr = RGB(255, 255, 255)
'Alternate background
With itemLblBackground
.Caption = ""
.BorderStyle = fmBorderStyleNone
.BackColor = altBgClr
.Left = 0
.Width = multiPage.Width
.Height = 35
.Top = itemTopPosition
End With
'display items
With itemLbl
.Caption = itemTable.Item(j, 4).Value
.BackStyle = fmBackStyleTransparent
.Font.Name = "Verdana"
.AutoSize = True
.Font.Size = 10
.Left = 6
.Width = itemLabelWidth
.Height = 30
.Top = (itemLblBackground.Top + (itemLblBackground.Height - itemLbl.Height) / 2) 'itemLblBackground.Top
End With
'==============================================================================================================
'
' LOW RADIO BUTTON
'
'==============================================================================================================
'Low radio button
Dim lowLabelName As String
Dim medLabelName As String
Dim highLabelName As String
Dim unknownLabelName As String
lowLabelName = "lowRadioGroup" & i & "_" & j
medLabelName = "medRadioGroup" & i & "_" & j
highLabelName = "highRadioGroup" & i & "_" & j
unknownLabelName = "unknownRadioGroup" & i & "_" & j
Set lowRadioButton = multiPage.Pages(i - 1).Controls.Add("Forms.OptionButton.1", lowLabelName)
With lowRadioButton
.Width = 20
.BackStyle = fmBackStyleTransparent
.Left = itemLabelWidth + 40
.Top = (itemLblBackground.Top + (itemLblBackground.Height - itemLbl.Height) / 2)
.GroupName = "RatingButton" & j
End With
RadioDictionary("Name") = lowLabelName
RadioDictionary("GroupName") = lowRadioButton.GroupName
RadioDictionary("Value") = lowRadioButton.Value
'==============================================================================================================
'
' MEDIUM RADIO BUTTON
'
'==============================================================================================================
Set medRadioButton = multiPage.Pages(i - 1).Controls.Add("Forms.OptionButton.1", medLabelName)
With medRadioButton
.Width = 20
.BackStyle = fmBackStyleTransparent
.Left = lowRadioButton.Left + lowRadioButton.Width + 40
.Top = (itemLblBackground.Top + (itemLblBackground.Height - itemLbl.Height) / 2)
.GroupName = "RatingButton" & j
End With
RadioDictionary("Name") = medLabelName
RadioDictionary("GroupName") = medRadioButton.GroupName
RadioDictionary("Value") = medRadioButton.Value
'==============================================================================================================
'
' HIGH RADIO BUTTON
'
'==============================================================================================================
Set highRadioButton = multiPage.Pages(i - 1).Controls.Add("Forms.OptionButton.1", highLabelName)
With highRadioButton
.Width = 20
.BackStyle = fmBackStyleTransparent
.Left = medRadioButton.Left + medRadioButton.Width + 40
.Top = (itemLblBackground.Top + (itemLblBackground.Height - itemLbl.Height) / 2)
.GroupName = "RatingButton" & j
End With
RadioDictionary("Name") = highLabelName
RadioDictionary("GroupName") = highRadioButton.GroupName
RadioDictionary("Value") = highRadioButton.Value
'==============================================================================================================
'
' DON'T KNOW RADIO BUTTON
'
'==============================================================================================================
Set unknownRadioButton = multiPage.Pages(i - 1).Controls.Add("Forms.OptionButton.1", unknownLabelName)
With unknownRadioButton
.Width = 22
.BackStyle = fmBackStyleTransparent
.Left = highRadioButton.Left + highRadioButton.Width + 45
.Top = (itemLblBackground.Top + (itemLblBackground.Height - itemLbl.Height) / 2)
.GroupName = "RatingButton" & j
End With
RadioDictionary("Name") = unknownLabelName
RadioDictionary("GroupName") = unknownRadioButton.GroupName
RadioDictionary("Value") = unknownRadioButton.Value
itemTopPosition = itemTopPosition + itemLblBackground.Height
k = k + 1
End If
Next j 'End item table range
'==============================================================================================================
'
' DISPLAY RATING OPTIONS AND LINE BOUNDARIES
'
'==============================================================================================================
'Adding a line in the headers
With multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
.Width = multiPage.Width - 10
.Height = 1
.Top = multiPage.TabFixedHeight + 16
.Left = multiPage.Left - 10
End With
'Adding a line between items and control
With multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
.Width = 1
.Height = multiPage.Height - multiPage.TabFixedHeight
.Top = 10
.Left = itemLabelWidth + 10
End With
'==============================================================================================================
'
' LOW
'
'==============================================================================================================
Set lowRatingLabelBg = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
'Low rating instruction background
With lowRatingLabelBg
.BackColor = RGB(244, 67, 54)
.Left = itemLabelWidth + 20
.Top = 12
.Width = 50
.Height = 25
End With
'Low rating instruction label
Set lowRatingLabel = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
With lowRatingLabel
.Caption = "LOW"
.BackColor = RGB(244, 67, 54)
.Width = 24
.Height = 12
.Left = lowRatingLabelBg.Left + (lowRatingLabelBg.Width - lowRatingLabel.Width) / 2
.Top = lowRatingLabelBg.Top + (lowRatingLabelBg.Height - lowRatingLabel.Height) / 2
.ForeColor = RGB(255, 255, 255)
End With
'Adding a line between items and control
With multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
.Width = 1
.Height = multiPage.Height - multiPage.TabFixedHeight
.Top = 10
.Left = lowRatingLabelBg.Width + lowRatingLabelBg.Left + 5
End With
'==============================================================================================================
'
' MEDIUM
'
'==============================================================================================================
Set medRatingLabelBg = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
'Med rating instruction
With medRatingLabelBg
.BackColor = RGB(255, 193, 7)
.Left = lowRatingLabelBg.Left + lowRatingLabelBg.Width + 10
.Top = 12
.Width = 50
.Height = 25
End With
'Low rating instruction label
Set medRatingLabel = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
With medRatingLabel
.Caption = "MEDIUM"
.BackStyle = fmBackStyleTransparent
.Width = 42
.Height = 12
.Left = medRatingLabelBg.Left + (medRatingLabelBg.Width - medRatingLabel.Width) / 2
.Top = medRatingLabelBg.Top + (medRatingLabelBg.Height - medRatingLabel.Height) / 2
.ForeColor = RGB(9, 9, 9)
End With
'Adding a line between items and control
With multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
.Width = 1
.Height = multiPage.Height - multiPage.TabFixedHeight
.Top = 10
.Left = medRatingLabelBg.Width + medRatingLabelBg.Left + 5
End With
'==============================================================================================================
'
' HIGH
'
'==============================================================================================================
Set highRatingLabelBg = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
'Med rating instruction
With highRatingLabelBg
.BackColor = RGB(46, 125, 50)
.Left = medRatingLabelBg.Left + medRatingLabelBg.Width + 10
.Top = 12
.Width = 50
.Height = 25
End With
Set highRatingLabel = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
With highRatingLabel
.Caption = "HIGH"
.BackStyle = fmBackStyleTransparent
.Width = 26
.Height = 12
.Left = highRatingLabelBg.Left + (highRatingLabelBg.Width - highRatingLabel.Width) / 2
.Top = highRatingLabelBg.Top + (highRatingLabelBg.Height - highRatingLabel.Height) / 2
.ForeColor = RGB(255, 255, 255)
End With
'Adding a line between items and control
With multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
.Width = 1
.Height = multiPage.Height - multiPage.TabFixedHeight
.Top = 10
.Left = highRatingLabelBg.Width + highRatingLabelBg.Left + 5
End With
'==============================================================================================================
'
' Don't Know
'
'==============================================================================================================
Set unknownRatingLabelBg = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
'Dont know rating instruction
With unknownRatingLabelBg
.BackColor = RGB(148, 176, 182)
.Left = highRatingLabelBg.Left + highRatingLabelBg.Width + 10
.Top = 12
.Width = 65
.Height = 25
End With
Set unknownRatingLabel = multiPage.Pages(i - 1).Controls.Add("Forms.Label.1")
With unknownRatingLabel
.Caption = "DON'T KNOW"
.BackStyle = fmBackStyleTransparent
.Width = 80
.Height = 12
.Left = unknownRatingLabelBg.Left + 9 + (unknownRatingLabelBg.Width - unknownRatingLabel.Width) / 2
.Top = unknownRatingLabelBg.Top + (unknownRatingLabelBg.Height - unknownRatingLabel.Height) / 2
.ForeColor = RGB(255, 255, 255)
End With
Next i 'End leader role table range
'Finally, get the actual page count
PageCount = multiPage.Pages.Count
End Sub
In regards of what I tried to suggest (in comments) I prepared a simple event wrapper class, which must be built in this way:
Insert a class module, name it optBClass and paste the next code inside it:
Option Explicit
Public WithEvents optEvent As MSForms.OptionButton
Private Sub optEvent_Change()
If optEvent.Name = "Opt1" Then
If optEvent.Value = True Then
testOptCreate.boolOpt1 = True
testOptCreate.boolOpt2 = False: testOptCreate.boolOpt3 = False
Else
testOptCreate.boolOpt1 = False
testOptCreate.boolOpt2 = False: testOptCreate.boolOpt3 = False
End If
ElseIf optEvent.Name = "Opt2" Then
If optEvent.Value = True Then
testOptCreate.boolOpt2 = True
testOptCreate.boolOpt1 = False: testOptCreate.boolOpt3 = False
Else
testOptCreate.boolOpt1 = False
testOptCreate.boolOpt2 = False: testOptCreate.boolOpt3 = False
End If
ElseIf optEvent.Name = "Opt3" Then
If optEvent.Value = True Then
testOptCreate.boolOpt3 = True
testOptCreate.boolOpt1 = False: testOptCreate.boolOpt2 = False
Else
testOptCreate.boolOpt1 = False
testOptCreate.boolOpt2 = False: testOptCreate.boolOpt3 = False
End If
End If
MsgBox optEvent.Name & " - " & optEvent.Value & vbCrLf & _
"boolOpt1 = " & testOptCreate.boolOpt1 & vbCrLf & _
"boolOpt2 = " & testOptCreate.boolOpt2 & vbCrLf & _
"boolOpt3 = " & testOptCreate.boolOpt3
End Sub
On top of the the form (module) you want to create option buttons, on the fly (testOptCreate in my exammple), please create the next variable (in the declarations part):
Option Explicit
Private optBColl As New Collection
Private OptionB() As New optBClass
Public boolOpt1 As Boolean, boolOpt2 As Boolean, boolOpt3 As Boolean
Create a button (btCreateOptB) and use this code for its Click event:
Private Sub btCreateOptB_Click()
Dim optCount As Long, theOptB As control, i As Long
optCount = 3
ReDim OptionB(0 To optCount)
For i = 1 To optCount
Set theOptB = Me.Controls.aDD("Forms.OptionButton.1", "Opt" & i, True)
With theOptB
.height = 17
.Caption = "Opt" & i
.left = 50 * i
End With
optBColl.aDD theOptB, theOptB.Name
Set OptionB(i).optEvent = theOptB
Next i
End Sub
Show the form and press btCreateOptB button!
Check each newly created option buttons. When first of them is clicked, the message box being shown display the clicked option button name, its value and the boolean variables associated to the group values.
Starting from the second option button click, the message will be shown twice. One time triggered by the previous option button change (its value becomes false) and second time referring to the last option button clicked.
Please, try understanding of its logic and try to apply it on your project.
If something not clear, do not hesitate to ask, even if I believe that it should be clear enough...
You can also use a Dictionary to automatically create boolean variables and use it to keep all of them and check when needed. Look, please at this test Sub:
Sub testDictionaryBooleanVar()
'it needs a reference to 'Microsoft Scripting Runtime`,
'or `dict` variable must be created `As Object` and create through `Set dict = CreateObject("scripting.dictionary")`
Dim it As Variant, dict As New Scripting.Dictionary, i As Long
With dict
For i = 1 To 10
.Item("boolOpt" & i) = False
Next i
End With
dict.Item("boolOpt2") = True: dict.Item("boolOpt5") = True
Debug.Print Join(dict.Items, "|")
Debug.Print dict("boolOpt2"), dict("boolOpt3")
End Sub
Presented this option in order to suggest a way to avoid manually creating boolean variables to be checked in the Option button Change event...

Handling Multiple UserForm Controls With One Event Handler - VBA Excel

I created many buttons dynamically (creating schedule) and want all of them do the same thing during Click event (OnClick property).
Of course, I can create max number of buttons on the form beforehand and set them invisible, and so forth, while adding "call SomeEvent" on their Click event considering that there can be over a thousand buttons. This would be very tedious.
Therefore, simplified:
I created new class btnClass`
Public WithEvents ButtonEvent As MsForms.CommandButton
Private Sub ButtonEvent_Click()
MsgBox "hey"
End Sub
Then, in my UserForm, where I dynamically create buttons I added this (I also have Collection, to remove buttons later), in its simplified form:
Dim btnColl As Collection
Dim Buttons As New btnClass
Set btnColl = New Collection
Set Buttons = New btnClass
For i = 0 To btnCount
Set theButton = Controls.Add("Forms.CommandButton.1", "btn" & i, True)
With theButton
.Height = 17
.Caption = "btn" & i
End With
Set Buttons.ButtonEvent = theButton
btnColl.Add theButton, theButton.Name
Next i
But nothing happens when I click dynamically created buttons. What am I missing?
---UPDATED
---#FaneDuru Provided solution which worked for me
ReDim Buttons(0 To btnCount, 0 To dtDiff)
For labelcounter = 0 To dtDiff 'add date labels
Set theLabel = Controls.Add("Forms.Label.1", "lblDay" & labelcounter, True)
With theLabel
.Caption = VBA.Format(DateAdd("d", labelcounter, bDate), "d-mm-yy")
.Left = 15 + 44 * labelcounter
.BackColor = vbBlack
.Font.Bold = True
.ForeColor = vbWhite
.Height = 13
.Width = 40
.Top = 85
End With
For i = 0 To btnCount 'add time buttons
pTime = DateAdd("n", i * dur, begTime)
Set theButton = Controls.Add("Forms.CommandButton.1", "btn" & CDate(theLabel.Caption & " " & TimeValue(pTime)), True)
With theButton
.Height = 17
.Caption = VBA.Format(TimeValue(pTime), "hh:mm")
'.Caption = CDate(theLabel.Caption & " " & TimeValue(pTime))
.Left = 15 + 44 * labelcounter
.BackColor = vbGreen
.Width = 40
.Top = 100 + 18 * i
End With
Set Buttons(i, labelcounter).ButtonEvent = theButton
btnColl.Add theButton, theButton.Name
Next i
Next labelcounter
In this way, only for the last created button an event is allocated. You must declare an array of classes... I also played a little with the Left property of the newly created buttons, only to have the possibility to test their click event. Try the next approach, please:
Option Explicit
Private btnColl As New Collection
Dim Buttons() As New btnClass
Private Sub btCreate_Click()
Dim btnCount As Long, theButton As CommandButton, i As Long
btnCount = 3
ReDim Buttons(0 To btnCount)
For i = 0 To btnCount
Set theButton = Me.Controls.aDD("Forms.CommandButton.1", "btn" & i, True)
With theButton
.height = 17
.Caption = "btn" & i
.left = 50 * i
End With
btnColl.aDD theButton, theButton.Name
Set Buttons(i).ButtonEvent = theButton
Next i
End Sub
Private Sub btdelete_Click() 'buttons deletion...
Dim i As Long
For i = 1 To btnColl.count
Me.Controls.Remove (btnColl(i).Name)
Next
End Sub

Checkbox Modification

I have a important question that I didn't find answer to, is it possible to increase the width and height of a check box and Is it possible to export to another sheet the check box with the values that were selected?
And is it possible to create multiple check boxes like for 500 or 1000 rows?
To your second question..............to populate multiple CheckBoxes by rows:
Sub BoxMaker()
For i = 1 To 4
ActiveSheet.CheckBoxes.Add(358.5, 50, 100, 60).Select
Next
Dim s As Shape
i = 2
For Each s In ActiveSheet.Shapes
s.Top = Cells(i, 1).Top
s.Height = Cells(i, 1).Height
s.Left = Cells(i, 1).Left
s.Width = Cells(i, 1).Width
i = i + 1
Next
End Sub
To your first question, just update the .Height and .Width
Regarding export, link the value of the checkboxes to any cells in the sheet and export these cell values ... there are plenty of descriptions here on SO how to export cell data.
To link a checkbox to a cell use
Sub test()
Dim S As Shape
Set S = ActiveSheet.Shapes(1)
S.ControlFormat.LinkedCell = "B1"
End Sub
To add this to Gary's Student's code ...
Sub BoxMaker()
For i = 1 To 4
ActiveSheet.CheckBoxes.Add(358.5, 50, 100, 60).Select
Next
Dim s As Shape
i = 2
For Each s In ActiveSheet.Shapes
s.Top = Cells(i, 1).Top
s.Height = Cells(i, 1).Height
s.Left = Cells(i, 1).Left
s.Width = Cells(i, 1).Width
' add Cell Link
' 2nd parameter of Cells(i, 2).Address sets column of linked cell ... in this case column B
s.ControlFormat.LinkedCell = Cells(i, 2).Address
i = i + 1
Next
End Sub
fields will be filled after first click to the checkboxes.

Resources