Adding controls to a frame in an Excel userform with VBA Adding controls to a frame in an Excel userform with VBA vba vba

Adding controls to a frame in an Excel userform with VBA


The following code demonstrates how you can dynamically populate a frame in a userform with controls...

In the form I used I had a frame control named Frame1, so in the UserForm_Initialize you call Frame1.Controls.Add to embed a control in the frame. You can set the control which gets returned to a WithEvents control variable that you have defined in the UserForm code module so you can respond to events on whatever controls you want...

So with this method you need to pre-write any event code you want for any controls you create...

Also note that you can position and size your controls even if the top, left, width, and height properties don't necessarily come up in intellisense...

Private WithEvents Cmd As MSForms.CommandButtonPrivate WithEvents Lbl As MSForms.LabelPrivate Sub UserForm_Initialize()    Set Lbl = Frame1.Controls.Add("Forms.Label.1", "lbl1")    Lbl.Caption = "Foo"    Set Cmd = Frame1.Controls.Add("Forms.CommandButton.1", "cmd1")End SubPrivate Sub Cmd_Click()    Cmd.Top = Cmd.Top + 5End SubPrivate Sub Lbl_Click()    Lbl.Top = Lbl.Top + 5End Sub


My variation on the theme above. This is just for a 4x4 array of buttons though. Create a userform and add this to its code. The same concepts can be used with your labels (or see the previous answer):

Private cmdLots(20) As MSForms.CommandButtonPrivate Sub UserForm_Initialize()For i = 1 To 4For j = 1 To 4    k = i + (4 * j)    Set cmdLots(k) = UserForm2.Controls.Add("Forms.CommandButton.1", "cmd1")    With cmdLots(k)        .Top = i * 25        .Left = (j * 80) - 50        .BackColor = RGB(50 * i, 50 * j, 0)        .Caption = "i= " & i & "  j= " & j    End WithNext jNext iEnd Sub


The Add Method

To add controls to a userform or a frame you use the add method.

SetControl = object.Add(ProgID [, Name [, Visible ]] )

The first argument is going to reference what type of control you want to add, and it is ProgID which is defined as

Programmatic identifier. A text string with no spaces that identifies an object class. The standard syntax for a ProgID is ... A ProgID is mapped to a class identifier (CLSID).

A Functional Solution

To make this process easier, let's use an enum to help manage the various controls for us.

' List of all the MSForms Controls.Public Enum MSFormControls    CheckBox    ComboBox    CommandButton    Frame    Image    Label    ListBox    MultiPage    OptionButton    ScrollBar    SpinButton    TabStrip    TextBox    ToggleButtonEnd Enum

With this enum, we can now easily create a function to get the ProgID string for all controls.

' Gets the ProgID for each individual control. Used to create controls using `Object.add` method.' @see https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/add-method-microsoft-formsPublic Function GetMSFormsProgID(control As MSFormControls) As String    Select Case control      Case MSFormControls.CheckBox:       GetMSFormsProgID = "Forms.CheckBox.1"      Case MSFormControls.ComboBox:       GetMSFormsProgID = "Forms.ComboBox.1"      Case MSFormControls.CommandButton:  GetMSFormsProgID = "Forms.CommandButton.1"      Case MSFormControls.Frame:          GetMSFormsProgID = "Forms.Frame.1"      Case MSFormControls.Image:          GetMSFormsProgID = "Forms.Image.1"      Case MSFormControls.Label:          GetMSFormsProgID = "Forms.Label.1"      Case MSFormControls.ListBox:        GetMSFormsProgID = "Forms.ListBox.1"      Case MSFormControls.MultiPage:      GetMSFormsProgID = "Forms.MultiPage.1"      Case MSFormControls.OptionButton:   GetMSFormsProgID = "Forms.OptionButton.1"      Case MSFormControls.ScrollBar:      GetMSFormsProgID = "Forms.ScrollBar.1"      Case MSFormControls.SpinButton:     GetMSFormsProgID = "Forms.SpinButton.1"      Case MSFormControls.TabStrip:       GetMSFormsProgID = "Forms.TabStrip.1"      Case MSFormControls.TextBox:        GetMSFormsProgID = "Forms.TextBox.1"      Case MSFormControls.ToggleButton:   GetMSFormsProgID = "Forms.ToggleButton.1"    End SelectEnd Function

And lastly, let's create a function that adds to a form or frame using our new function.

' Easly add control to userform or a frame.' @returns {MSForms.control} The control that was createdPublic Function AddControl(userformOrFrame As Object _                         , control As MSFormControls _                         , Optional name As String = vbNullString _                         , Optional visable As Boolean = True _                        ) As MSForms.control    Set AddControl = userformOrFrame.Controls.Add(GetMSFormsProgID(control), name, visable)End Function

The beauty of using enums like this is that we now have a intellisense for all the controls and don't have to memorize them all.

Demo showing intellisense

Demo

To demo it, we can add every control to a blank userform by looping the enum.

Private Sub UserForm_Initialize()    demoAddingControlsToUserformEnd SubPrivate Sub demoAddingControlsToUserform()    ' Offset used to prevent controls    ' overlapping as well as provide    ' a height for the scrollbars    Dim offsetHeight As Double        ' Add each control to the userform    ' and set top to make sure they are not overlapping    ' (Although this looks odd, you can actually loop enums this way.)    Dim control As MSFormControls    For control = CheckBox To ToggleButton        With AddControl(Me, control)            .Top = offsetHeight            offsetHeight = offsetHeight + .Height        End With    Next        ' Show scrollbars and adjust the height to show    ' all the added controls.    With Me        .ScrollBars = fmScrollBarsVertical        .ScrollHeight = offsetHeight + 20    End WithEnd Sub

Demo Userform with all controls