0

This is a followup on my first question:

Through a click event, I dynamically added some elements (txtBox01 and cmdButton01) to the previously empty (static) UserForm1. Now I want to change the textbox's content through the click event of cmdButton01. How exactly do I have to reference cmdButton01?

Here's how I create the dynamic elements (simplified!):

Private Sub CommandButton1_Click()    
    
    Dim cmdArray() As New Class1
    i = 1       
        
        'Layout for static Form
            'Set Formsize / Formtitle
                UserForm1.Height = 130
                UserForm1.Width = 300

            'Create Form-Elements (TextBox1)
                Dim txtBox01 As MSForms.TextBox
                Set txtBox01 = UserForm1.Controls.Add("Forms.TextBox.1", "dynTxtBox_01")
                txtBox01.Top = 10
                txtBox01.Left = 10
                txtBox01.Width = 200
                txtBox01.Text = "something"

            'Create Form-Elements (Commandbutton)
                Dim cmdButton01 As MSForms.CommandButton
                Set cmdButton01 = UserForm13.Controls.Add("Forms.CommandButton.1", "dynCmdButton01", False)
                cmdButton01.Top = 70
                cmdButton01.Left = 10
                cmdButton01.Width = 200
                cmdButton01.Caption = "Save"
                cmdButton01.Visible = True

                ReDim Preserve cmdArray(1 To i)
                Set cmdArray(i).CmdEvents = cmdButton01
                Set cmdButton01 = Nothing                    

        'Show Form
            UserForm1.Show

    End Sub

I assigned the code for the click event through the following code. But I'm not sure how to reference the dynamic elements on the static form. I tried a few examples I found on the web but nothing worked:

Public WithEvents CmdEvents As MSForms.CommandButton    
Private Sub CmdEvents_Click()

    'Simple Test (works fine)
        MsgBox "Test1"

    'Change the Text of TextBox01 (this one is PSEUDO code to illustrate what I want to do)
         UserForm1.txtBox01.Text= "123"       
         '=> how should I reference the dynamic form element to make this work??
         
     'Close Form
        UserForm1.Hide

    End Sub
Albin
  • 1,000
  • 1
  • 11
  • 33
  • 1
    You can use the following syntax `UserForm1.Controls("dynTxtBox_01").Text = "123"`. – Brian M Stafford Aug 19 '20 at 20:38
  • @BrianMStafford Works fine, the only problem is that I have to use `Unload UserForm1` otherwise - in a modified form - it only works on the "first call". Any idea why that is? – Albin Aug 19 '20 at 23:13
  • @BrianMStafford btw, feel free to post it as an answer... – Albin Aug 20 '20 at 10:27

2 Answers2

1

Use the next approach, please:

  1. Insert a Class module, name it clsBtn and copy the next code:
Option Explicit

Public WithEvents cmdButton As MSForms.CommandButton

Public Sub cmdButton_Click()
    Dim ans As String
    ans = InputBox("What to write in the newly created text box?", _
                            "Write some text, please", "Default")
    If ans <> "" Then
      cmdButton.Parent.txtBox01.Text = ans
    End If
End Sub
  1. On top of the Form module, in the declarations area, paste the next variables declaration:
Public txtBox01 As MSForms.TextBox
Private cmdButton01 As MSForms.CommandButton
Private ButtColl As New Collection
Private cmdButt(0) As New clsBtn
  1. Your CommandButton1_Click event will look like this:
Private Sub CommandButton1_Click()
    Set txtBox01 = Me.Controls.Add("Forms.TextBox.1", "dynTxtBox_01")
    With txtBox01
        .top = 10
        .left = 10
        .width = 200
        .Text = "something"
    End With
    
    Set cmdButton01 = Me.Controls.Add("Forms.CommandButton.1", "dynCmdButton01", False)
    With cmdButton01
        .top = 70
        .left = 10
        .width = 200
        .Caption = "Save"
        .Visible = True
    End With
    
    ButtColl.Add cmdButton01, cmdButton01.Name
    Set cmdButt(0).cmdButton = cmdButton01
End Sub
  1. Load the form, click CommandButton1 and then click the newly created button ("Save" Caption). It will change the newly created text box from "something" in "Changed"...
FaneDuru
  • 38,298
  • 4
  • 19
  • 27
  • Any specific reason why it's better to use your approach? I found that using `UserForm1.Controls("dynTxtBox_01").Text = "123"` works fine. (Just a minor problem that I had to use `Unload UserForm1` otherwise it stops working after the first call) – Albin Aug 19 '20 at 23:11
  • @ Albin: Twelve hours before you asked a question. Eleven hour before i tried showing how to handle the newly created text box value change. If, in the meantime, you found a better alternative, feel free to use it! And the answer to the above question would be the next: Using `cmdButton.Parent.txtBox01.Text = "Changed"` will offer you the possibility to change your form name, from default `UserForm1` to, let us say, `MyForm` without changing the class code. The form object is returned by `cmdButton.Parent`, independent of the form name. And "this approach" meant a little more... – FaneDuru Aug 20 '20 at 06:44
  • @ Albin: I updated the above answer to offer the possibility to choose the text to be passed to the newly created text box. – FaneDuru Aug 20 '20 at 06:51
  • Sorry, didn't mean to sound ungrateful, just wanted to ask since there was no direct comment on what the benefits are. I got "my" solution from Brian (see comments to the question). I'll have a look at yours now and let you know how it works. – Albin Aug 20 '20 at 08:29
  • @ Albin: I do not have any problem! I only wanted to emphasize that "my approach" suggestion came when no any alternative existed, you only saying: "nothing worked". The "approach" involves three different steps. A **class wrapper** is necessary to create events for a newly added control. And the best way of referring (I think) is to do it in a way to not be affected by a future form name change... But, of course, my solution is only on example to show how you can achieve what you requested. It certainly can be improved and you should use whatever looks being more suitable for your purpose. – FaneDuru Aug 20 '20 at 09:29
  • ok, first question... why do you use "With". I know it's supposed to give better performance, but I didn't find much difference (on modern laptops) and since the code is harder to read, I usually don't use it. Or is there another reason as well? – Albin Aug 20 '20 at 10:24
  • @ Albin: Not the better performance was the reason... Instead of repeating `cmdButton01` five times, a simplified syntax makes the code more readable. And looking more neat. This is not part of "approach"... :) But you did not say anything about the code itself. Does it work? – FaneDuru Aug 20 '20 at 10:37
  • Completely forgot to look over the code. I'm sort of confused by the 3rd step, why is the click event creating the textbox and the button? Both elements have been created already, the click-event is just suppose to change the textbox's text. Was there a missunderstanding here? – Albin Oct 25 '20 at 05:19
  • @ Albin: The third step simple is your code adapted to use the event wrapper class. Nothing has been created "already". They are created in this very step. The newly created button click event is created in the first step. This "approach" allows you to create as many buttons as you want and allocate to them the same click event. The event can be adapted according to the button name, anyhow. But didn't your interest for understanding this piece of code come a little late? – FaneDuru Oct 25 '20 at 14:12
  • "come a little late?" As a dirty workaround I used Brian's solution, but I like the idea for managing the code from runtime buttons in separate classes, it's a little bit cleaner and it's easier to reuse them. I just didn't get around to it yet. – Albin Oct 25 '20 at 15:54
  • But I think there was some missunderstanding, the dynamic elements should already exist on the userform at this point during the runtime. Pressing the button just changes the caption/text of one of them. – Albin Oct 25 '20 at 15:56
  • @ Albin: Not exactly... Only the wrapper class exists and „wait” for a control to be created and its click event to be allocated to it. That „third step” creates the control. **Instead of the way you used**. In fact, it is the same, only the way of using the wrapper class is in plus: `ButtColl.Add cmdButton01, cmdButton01.Name` `Set cmdButt(0).cmdButton = cmdButton01`... – FaneDuru Oct 25 '20 at 16:20
  • @ FaneDuru: Then I think I still don't get what `Set cmdButton01 = Me.Controls.Add(...)` is doing. Isn't this creating the command button? If not, what is it creating? A second command button? I'm not not really sure what you are trying to show here (since a second button is not part of the question, in my example there should be only one button changing the label text when clicked). Sorry for being a little slow here, I appreciate your patience though, thx. – Albin Oct 31 '20 at 13:54
  • 1
    @ Albin: Of course, it does. That line belongs to your original code. The third step only **replaces your original code** with one able to allocate an event to the newly created button. In these lines: `ButtColl.Add cmdButton01, cmdButton01.Name` `Set cmdButt(0).cmdButton = cmdButton01`. – FaneDuru Oct 31 '20 at 14:38
  • @ FaneDuru: oh, sorry this has been too long. I forgot that I created the whole thing via a Button (that changed by now). Sorry about that. – Albin Oct 31 '20 at 14:48
1

To answer your specific question, the syntax would be like the following:

UserForm1.Controls("dynTxtBox_01").Text = "123"
Brian M Stafford
  • 8,483
  • 2
  • 16
  • 25