0

I have this userform

enter image description here

  1. The form contains three comboboxes along with "add new" command button

  2. Once user click add new button it will add another row of comboboes with a clear button.

  3. The clear button will remove that particular row of comboboxes only. add button will add a new line below this line

  4. each row is an element i.e A,B,C... The insert button will write conditions e.g. A OR B OR C inserted text gathered from comboboxes would be {#(ProductA === "P25" || ProductB === "P26" || ProductC === "P27")}

I am trying to have a button that can dynamically add new line of comboboxes with a clear button. also clear button that will only remove that line of comboboxes. I can user the code as discussed here. But don't know how I can handle the position and clear button.

VBAbyMBA
  • 806
  • 2
  • 12
  • 30
  • It would help your question if you include the exact code you're trying to use, and describe the specific issue you have with it (what exactly is preventing you from achieving what you want?) – Tim Williams Dec 09 '21 at 08:16
  • @TimWilliams I will rephrase it – VBAbyMBA Dec 09 '21 at 08:22
  • I would suggest you create all controls already and just hide those that are not needed. When user presses the Add-Button, you simply show the controls. – FunThomas Dec 09 '21 at 09:19
  • @FunThomas yes I have done the same but it will limit the number of row. – VBAbyMBA Dec 09 '21 at 09:38
  • 1
    If you really want to create & remove controls at runtime, you are facing a lot of things you need to solve. Create a control, attach events to a control, handle scrolling when necessary, rearrange position of controls when you remove a "row". When a control is created in, lets say, row 4, it need to know that it is on row 4: How do you manage that. And when row 3 is removed, the former row 4 gets now row 3: You need to handle this also. All of this is solvable but is by far to broad as a question on SO. – FunThomas Dec 09 '21 at 09:55
  • How the created on the fly combos to be loaded? Based on what? To be loaded during creation or using a different control? How your code to 'know' which combo row to be processed? I mean, based on what? Will all of them be processed globally? Do you need any of the created combos event (change etc.)? – FaneDuru Dec 09 '21 at 15:09
  • I agree this is far too broad/complex for us to be able to offer any code which would do this. However, I'd suggest following Sid's answer from your linked question, but creating your control array class to hold the 3 combo boxes and "clear" button for each row, and the code to manage responding to user actions on that row. Use an array or collection to store instances of the class as you create them them at runtime. The "AddNew" button could just be a regular button which gets relocated as you add/remove rows. – Tim Williams Dec 09 '21 at 18:07

1 Answers1

1

An interesting mini-project, so here is my "solution" (obviously still far from being fully-functional but has the basic add/remove row capability.

The userform I tested with had only a single command button btnAddRow (for adding new rows).

Class DataRow:

Option Explicit

Private WithEvents Combo1 As MSForms.ComboBox
Private WithEvents Combo2 As MSForms.ComboBox
Private WithEvents ClearButton As MSForms.CommandButton

Dim controlContainer As Object
Public id As Long

Private Sub ClearButton_Click()
    'calls a method on the parent form to remove the row
    CallByName controlContainer, "RemoveRow", VbMethod, id
End Sub


'create and set up a few properties the controls for a row
Public Sub Create(container As Object, rowId As Long)
    
    Set Combo1 = container.Controls.Add("Forms.ComboBox.1", "C1_" & rowId)
    Combo1.Left = 20
    Combo1.Width = 60
    Combo1.List = Array("Item1", "Item2")
    
    Set Combo2 = container.Controls.Add("Forms.ComboBox.1", "C2_" & rowId)
    Combo2.Left = 90
    Combo2.Width = 60
    Combo2.List = Array("Red", "Blue")
    
    Set ClearButton = container.Controls.Add("Forms.commandbutton.1", "BC_" & rowId)
    ClearButton.Left = 160
    ClearButton.Width = 50
    ClearButton.Height = 20
    ClearButton.Caption = "Clear"
    
    Set controlContainer = container 'keep a reference to the place where the controls are created
    id = rowId                       'identifies this row
End Sub

'set the vertical position on the form for the row
Public Sub Position(rowIndex)
    Dim pTop
    pTop = 15 + (25 * (rowIndex - 1))
    Combo1.Top = pTop
    Combo2.Top = pTop
    ClearButton.Top = pTop
End Sub

'remove the row - called from the parent form
Public Sub Remove()
    With controlContainer.Controls
        .Remove Combo1.Name
        .Remove Combo2.Name
        .Remove ClearButton.Name
    End With
End Sub

'move the "add row" button to this row
Public Sub TakeAdd(btn As MSForms.CommandButton)
    btn.Left = ClearButton.Left + ClearButton.Width + 10
    btn.Top = ClearButton.Top
End Sub

'is this row removable? (last remaining row can't be removed)
Public Sub CanRemove(bRemovable As Boolean)
    ClearButton.Enabled = bRemovable
End Sub

The code for the userform:

Option Explicit

Dim id As Long              'sequence number for assigning unique id to each row
Dim col As New Collection   'holds instances of the class `DataRow`

Private Sub btnAddRow_Click()
    AddRow 'add a new row
End Sub

Private Sub UserForm_Activate()
    AddRow 'add starting row
End Sub

'add a row
Sub AddRow()
    Dim obj As New DataRow
    id = id + 1
    obj.Create Me, id          'creates the controls
    col.Add obj                'add to the collection
    obj.Position col.Count     'sets row index on form
    obj.TakeAdd Me.btnAddRow   'position the "add row" button
    checkRemove                'check if remove buttons are enabled
End Sub

'remove the specified row
Sub RemoveRow(rowId As Long)
    Dim i As Long
    i = 1
    Do While i <= col.Count
        If col(i).id = rowId Then   'is this the row to remove?
            col(i).Remove           'removes the row from the form
            col.Remove (i)          'removes the class instance from the collection
        Else
            col(i).Position i       '(re)position this row
            i = i + 1
        End If
    Loop
    col(col.Count).TakeAdd Me.btnAddRow '(re)position the "add row" button
    checkRemove        'check if remove buttons are enabled
End Sub

'see if the "remove" buttons should be enabled
Sub checkRemove()
    Dim obj
    For Each obj In col
        obj.CanRemove (col.Count > 1) 'enabled if >1 row on form
    Next obj
End Sub
Tim Williams
  • 154,628
  • 8
  • 97
  • 125