I'm building a workout logging application that allows the user to add a movement, number of reps, and the associated weight while the userform is running. (You can also think of it as "order-picking" eg. "3" "red" "pencils", "2" "blue" "markers", etc.)
The code adds the two textboxes and the combo box when the "+" button is clicked.
When the "-" button is clicked, only the last added group of controls will be deleted but if the "-" button is clicked again I'll get the "Catastrophic Failure" error message.
I'm pretty sure these controls need to be deleted using controls.remove (object).name but once that last group is deleted, I'll need to update the object variable to the next group that could be removed which I'm struggling with.
I found similar questions on here with answers, but those deleted all userform controls. I only want to remove the last group added.
Option Explicit
Public movementCounter As Integer
Public repsTextBox As Object
Public movementComboBox As Object
Public weightTextBox As Object
Public cntrlsColl As Collection
Private Sub addMvmtCmndButt_Click()
movementCounter = movementCounter + 1
Set repsTextBox = Controls.Add("Forms.TextBox.1")
Set movementComboBox = Controls.Add("Forms.ComboBox.1")
Set weightTextBox = Controls.Add("Forms.TextBox.1")
With repsTextBox
.Name = "RepsTextBox" & movementCounter
.Height = 18
.Width = 45
.Left = 10
.Top = 30 * (movementCounter - 1) + 5
End With
With movementComboBox
.Name = "MovementComboBox" & movementCounter
.Height = 18
.Width = 90
.Left = 65
.Top = 30 * (movementCounter - 1) + 5
.RowSource = listsSht.Range("movementList").Address
End With
With weightTextBox
.Name = "WeightTextBox" & movementCounter
.Height = 18
.Width = 45
.Left = 165
.Top = 30 * (movementCounter - 1) + 5
End With
End Sub
Private Sub deleteMvmtCmndButt_Click()
'Works, but only for the most recently added group of controls
With Me.Controls
.Remove repsTextBox.Name
.Remove movementComboBox.Name
.Remove weightTextBox.Name
End With
'update what the next group of controls will be on-deck
' If movementCounter > 0 Then
' repsTextBox.Name = "RepsTextBox" & movementCounter
' movementComboBox.Name = "MovementComboBox" & movementCounter
' weightTextBox.Name = "WeightTextBox" & movementCounter
' End If
End Sub
Try the following code...
Private Sub deleteMvmtCmndButt_Click()
If movementCounter > 0 Then
With Me.Controls
.Remove "RepsTextBox" & movementCounter
.Remove "MovementComboBox" & movementCounter
.Remove "WeightTextBox" & movementCounter
End With
movementCounter = movementCounter - 1
End If
End Sub
Hope this helps!
Related
I am trying to add _Change() event to dynamically created TextBox using classes in VBA. However there is nothing happening, when I try to run my code. Could you please point me where I am wrong?
I have got class conditionEventClass
Public WithEvents conditionEvent As MSForms.textBox
Public Property Let textBox(boxValue As MSForms.textBox)
Set conditionEvent = boxValue
End Property
Public Sub conditionEvent_Change()
MsgBox conditionEvent.Name & " changed."
End Sub
I have got following code in my module:
Sub addConditions()
Dim conditionCommand As conditionEventClass
Dim newTextBox As MSForms.textBox
Set newTextBox = commandRequestForm.MultiPage1(1).Controls.Add("Forms.TextBox.1", "conditionValue", True)
With newTextBox
.Name = "conditionValue"
.Left = 750
.height = 15
.Width = 100
.Top = 20
End With
Set conditionCommand = New conditionEventClass
conditionCommand.textBox = newTextBox
End Sub
I expect that my sub conditionEvent_Change() is going to show msgBox. But unfortunately nothing happens.
Talking about only a single Text Box, you can use the next simpler way:
1.Declare a private variable on top of the form code module (in the declarations area):
Private WithEvents myTextBox As MSForms.TextBox
Then, create the event for the above declared variable:
Private Sub myTextBox_Change()
MsgBox activecontrol.name & " changed."
End Sub
Use your adapted code as:
Sub addConditions()
Dim newTextBox As MSForms.TextBox
Set newTextBox = commandRequestForm.MultiPage1(1).Controls.Add("Forms.TextBox.1", "myTextBox", True)
With newTextBox
.left = 10
.height = 15
.width = 100
.top = 20
End With
Set myTextBox = newTextBox
End Sub
For 1 to 3, 4 such controls you can use the simpler (above shown) way. If you need creating on the fly a lot of such controls, I can show you how to adapt your code...
Edited:
Please, use the next working way using a class to be assigned to many text boxes created on the fly:
Copy the next code in a class module and name it 'clsTBox':
Option Explicit
Public WithEvents newTBox As MSForms.TextBox
Private Sub newTBox_Change()
MsgBox newTBox.name & " changed."
End Sub
2.Declare a Private variable on top of the form code module:
Private TBox() As New clsTBox
Use the next Sub to create three text boxes and assign the Click event to them:
Private Sub CreateThreeTB()
Dim i As Long, txtBox01 As MSForms.TextBox, leftX As Double, tWidth As Double, k As Long
leftX = 20: tWidth = 50
ReDim TBox(100) 'use here the maximum number of text boxes you intend creating
For i = 1 To 3
Set txtBox01 = Me.Controls.Add("Forms.TextBox.1", "dynTxtBox_" & i)
With txtBox01
.top = 10
.left = leftX: leftX = leftX + tWidth
.width = tWidth
.Text = "something" & i
End With
Set TBox(k).newTBox = txtBox01: k = k + 1
Next i
ReDim Preserve TBox(k - 1)
End Sub
Call the above Sub from Initialize event or from another control, play with the newly created text boxes value and see how the change event is triggered...
I create a form dynamically and fill it with check boxes generated based on all column names of the Excel sheet it is launched from.
I add also a command button.
Here is the code put directly on the form:
Option Explicit
Dim cmdArray() As New Class1
Private Sub UserForm_Initialize()
Dim lastCol As Integer
Dim i As Integer
Dim chkBox As MSForms.CheckBox
Dim myButton As Control
lastCol = Worksheets(1).Cells(1, Columns.Count).End(xlToLeft).Column
Me.Height = 500
Me.Width = 600
For i = 1 To lastCol
Set chkBox = Me.Controls.Add("Forms.CheckBox.1", i)
chkBox.Caption = Worksheets(1).Cells(1, i).Value
' chkBox.Name = i
If i Mod 2 = 1 Then
chkBox.Left = 5
chkBox.Top = 5 + (i - 1) * 10
chkBox.Width = 200
Else
chkBox.Left = 250
chkBox.Top = 5 + (i - 2) * 10
chkBox.Width = 200
End If
Next i
i = 1
Set myButton = Me.Controls.Add("Forms.CommandButton.1", "MyButton", False)
With myButton
.Left = 500
.Top = chkBox.Top - 50
.Width = 50
.Caption = "Hide"
.Visible = True
End With
ReDim Preserve cmdArray(1 To i)
Set cmdArray(i).CmdEvents = myButton
Set myButton = Nothing
Me.ScrollBars = fmScrollBarsVertical
Me.ScrollHeight = chkBox.Top + 20
End Sub
The form is generated without issue: all check-boxes and the command button are set correctly.
I am then supposed to select which columns I want to hide from my Excel sheet, and therefore I tick the relevant checkbox. So far so good. Here is the code set is a Class :
Option Explicit
Public WithEvents CmdEvents As MSForms.CommandButton
Private Sub CmdEvents_Click()
Dim i As Integer
Dim cbx As MSForms.Control
Dim colNum As Integer
i = 0
For Each cbx In Me.UserForm1.Controls
If TypeName(cbx) = "CheckBox" Then
If cbx.Value = True Then
colNum = cbx.Name - i
Worksheets(1).Columns(colNum).EntireColumn.Delete
i = i + 1
End If
End If
Next ctrl
End Sub
When I click the button, it is supposed to trigger the hiding of the columns in the Excel sheet, however, I got the following error:
Compile error: Method or data member not found
This error is reported in the code in the Class module, highlighting the term .UserForm1 and if I remove this .UserForm1, then still the same error highlighting the .Controls.
I am not a great specialist of VBA, I manage usually to create simple codes and reusing samples I can find here and there, but this time, I run out of idea (and understanding), so thanks in advance for any help.
I am working on a Userform in which one can add and remove Textboxes. These TextBoxes then can be filled out and by using a CommandButton SaveData the content should then safe in the sheet.
In order to do so, I declared a gloabal variable nFarbe. Using this variable I am controlling the number of Texboxes. The weird thing is, by initialising the Form calling LoadTextBox and ReadTextBoxContent the correct number of boxes are loaded and the content is filled in. Howevern, by adding a TextBox AddColor the nFarbe is increased by 1 in Order to add a TextBox. The additional TextBox shows up, but the content of all boxes but the new one vanishes. This I do not understand since I'm calling LoadTextBox and ReadTextBoxContent as I do during the initialization. By calling RemoveColor nFarbeis reduced by 1 in order to delete 1 TextBox. This does not work at all.
Any thouhts? Help is much appreciated!
Private Sub Userform_Initialize()
nFarbe = 1
Call LoadTextBox
Call ReadTextBoxContent
End Sub
Private Sub AddColor_Click()
nFarbe = nFarbe + 1
Call LoadTextBox
Call ReadTextBoxContent
TextBox_nFarbe = nFarbe
End Sub
Private Sub RemoveColor_Click()
nFarbe = nFarbe - 1
Call LoadTextBox
Call ReadTextBoxContent
TextBox_nFarbe = nFarbe
End Sub
Private Sub SaveData_Click()
Dim z As Double
z = 0
For i = 1 To nFarbe
z = 31 + i
Cells(z, 1).Value = Me("ProductColor" & i).Text
Next i
End Sub
Sub LoadTextBox()
Dim NewColorTextbox As Variant
Dim tp As Double
tp = 25
For i = 1 To nFarbe
Set NewColorTextbox = Me.Controls.Add("Forms.TextBox.1", "MyTextBox", True)
With NewColorTextbox
.Name = "ProductColor" & i
.Width = 150
.Height = 18
.Top = tp
.Left = 30
.ZOrder (0)
.Font.Size = 10
End With
tp = tp + 25
Next i
End Sub
Sub ReadTextBoxContent()
Dim z As Double
For i = 1 To nFarbe
z = 31 + i
Me("ProductColor" & i) = Cells(z, 1)
Next i
End Sub
I have the following which add 1 editBox and 1 comboBox every time I click on a button in a userForm :
Sub addCable()
'TextBox creation
Dim editTxtBox As MSForms.Control
Static iTxtBox As Integer
Set editTxtBox = Me.Controls.Add("Forms.TextBox.1")
iTxtBox = iTxtBox + 1
cbxs.Add editTxtBox
', editTxtBox.Name
With editTxtBox
.Name = "txtBox" & iTxtBox
.Top = iTxtBox * editTxtBox.Height + 10
.Left = 20
End With
'ComboBox creation
Dim editCmpBox As MSForms.Control
Static iCmpBox As Integer
Set editCmpBox = Me.Controls.Add("Forms.ComboBox.1")
iCmpBox = iCmpBox + 1
cbxs.Add editCmpBox
', editCmpBox.Name
With editCmpBox
.Name = "cmBox" & iCmpBox
.Top = iCmpBox * editCmpBox.Height + 10
.Left = 130
.List = Array("60", "125", "45")
End With
End Sub
I want to implement the undo function, as it delete the last exitBox and the last comboBox created when I click on a button.
I was thinking to loop over my cbxs collection and remove at the index iTxtBox as iTxtBox is a global variable :
Dim num As Integer
MsgBox (iTxtBox)
For num = iTxtBox To cbxs.Count
cbxs.Remove iTxtBox
Next
But it doesn't work, I guess it is because my loop is out of the scope I'm looking for....
I have a form that sets up a list of labels with content and an accompanying checkbox on initialisation.
I want to check the value of a checkbox when a button is clicked.
How do I reference back to the checkbox - I have called the checkbox a number (the value of i) when they are created.
Code to add the checkbox:
Sub addLabel()
Dim theCheck As Object
Dim theLabel As Object
Dim i As Long
Dim LastRow As Integer
LastRow = Worksheets("Assumptions").Cells(Rows.Count, "B").End(xlUp).Row
For i = 1 To LastRow
Set theLabel = UserForm1.Controls.Add("Forms.Label.1", "Assumption" & i, True)
With theLabel
.Name = "Assumption" & i
.Caption = Worksheets("Assumptions").Range("B" & i).Value ' & labelCounter
.Left = 156
.Width = 500
.Top = 138 + i * 20
End With
Set theCheck = UserForm1.Controls.Add("Forms.CheckBox.1", i, True)
With theCheck
.Name = i
.Left = 140
.Width = 10
.Top = 138 + i * 20
End With
Next
End Sub
My ultimate goal is to check which checkbox is 'True' and then IF true enter the accompanying label content into a worksheet.
My main struggle at the moment is how to reference the checkboxes by name (e.g. loop through them all where they are called 1-10 for example.
Thanks
To make reference of an object in your form you can use the following syntax
<Name of your form>.<Name of your control>
In you can I believe that something like UserForm1.1 but this is not a great idea to call your checkbox only with a number, give it with a proper name.
I strongly recommend that you change
With theCheck
.Name = i 'This is not great
.Left = 140
.Width = 10
.Top = 138 + i * 20
End With
By something more explicit like
With theCheck
.Name = "cb" & i 'Not great but better
.Left = 140
.Width = 10
.Top = 138 + i * 20
End With
Loop through each Checkbox in your form
To go through each Checkbox and check if it's checked or not, you can use something like this
'Go through each control in your UserForm
For Each myControl In UserForm1.Controls
'If the current control is a Checkbox
If (TypeName(myControl) = "Checkbox") Then
'Check it's value
If (myControl.Value = True) Then
'Do whatever you want
'You can access your checkbox properties with myControl.YOUR_PROPERTY
End If
End If
Next myControl