Excel VBA Textbox click event on Userform - excel

On a EXCEL Userform, I have several Textboxes. Their number can vary as they are created dynamically.
I want to achieve the following:
When the user clicks on any of the Textboxes, I want to display a Msgbox, but only on this particular userform and only once for the first click.
Could you give me a pointer for documentation that would help me achieve this?
after googling on this, my code looks like this:
Userform: create a variable number of textboxes
Option Explicit
Dim oKlasseExcel() As Klasse1
Sub userform_initialize()
Dim i As Long
Dim k As Long
k = InputBox("insert number")
i = 0
Do
ReDim oKlasseExcel(0 To i)
Set oKlasseExcel(i) = New Klasse1
Set oKlasseExcel(i).objTextbox = Userform1.Controls.Add("Forms.Textbox.1", "Textbox" & CStr(i))
With oKlasseExcel(i).objTextbox
.Left = 30
.Top = 75 + 25 * i
.Width = 300
.Height = 25
.MultiLine = True
End With
i = i + 1
Loop Until i = k
End Sub
class module:
Option Explicit
Public WithEvents objTextbox As MSForms.TextBox
Sub objTextbox_click()
MsgBox objTextbox.Name & ": Changeereignis ausgelöst!"
End Sub
I think I have to create a class module probably, but I am totally new to this and I think I need a well-written example with some explanation comments, please. My code above does nothing when I click on a textbox.

If you don't want to create any additional data structures to capture clicks, maybe you can try changing some TextBox properties, for example:
create a TextBox with WordWrap = False and after it was clicked change its value to True - then you could distinguish which were clicked or not.

Related

How to add a label and textbox based on combo box selection on a userform in excel

I am currently working on a userform to create an order for users at a company to send to my dept.
At the moment i have come to a standstill as i am struggling to work out the following.
I have a combobox which has a list of products our business offers. Based on the selection i want to be able to add labels and textbox which require the user to enter data for example.
If this selection in the combo box then
Enter name, date required, location of user etc.
Also this needs to be specific to the combobox selection.
Any help would be much appreciated :)
UPDATE
Apologies, as i did not have any code for that function I did not add any Here is the code i have.
Private Sub CommandButton1_Click()
Windows("RFS User Form Mock.xlsm").Visible = True
End Sub
Private Sub LegendDefinition_Change()
LegendDefinition.Locked = True
End Sub
Private Sub RequestList_Change()
Dim i As Long, LastRow As Long
LastRow = Sheets("Definition").Range("A" & Rows.Count).End(xlUp).Row
For i = 2 To LastRow
If Sheets("Definition").Cells(i, "A").Value = (Me.RequestList) Then
Me.DefinitionBox = Sheets("Definition").Cells(i, "B").Value
End If
Next
End Sub
Private Sub RequestList_DropButtonClick()
Dim i As Long, LastRow As Long
LastRow = Sheets("Definition").Range("A" & Rows.Count).End(xlUp).Row
If Me.RequestList.ListCount = 0 Then
For i = 2 To LastRow
Me.RequestList.AddItem Sheets("Definition").Cells(i, "A").Value
Next i
End If
End Sub
Sub UserForm_Initialize()
SiteList.List = Array("Birmingham", "Bristol", "Cardiff", "Chelmsford", "Edinburgh", "Fenchurch Street", "Glasgow", "Guernsey", "Halifax", "Homeworker", "Horsham", "Ipswich", "Jersey", "Leeds", "Leicester", "Lennox Wood", "Liverpool", "Manchester", "Peterborough", "Redhill", "Sunderland", "Madrid")
End Sub
Private Sub VLookUp_Change()
VLookUp.Locked = True
End Sub
When posting a question, you are expected to provide some code showing where you're standing trying to address the problem. Here's nevertheless a short demo that will give you a starting point.
Create a new UserForm and put a combobox, a label and a textbox on it; ensure they're named ComboBox1, Label1 and TextBox1, respectively.
Then, paste this code in the form's module:
Option Explicit
Private Sub ComboBox1_Change()
Dim bVisible As Boolean
'Only show the label and the textbox when the combo list index is 1, which corresponds to "Item 2".
'Note: bVisible = (ComboBox1.Text = "Item 2") would also work.
bVisible = (ComboBox1.ListIndex = 1)
Label1.Visible = bVisible
TextBox1.Visible = bVisible
End Sub
Private Sub UserForm_Layout()
'Populate the combo.
ComboBox1.AddItem "Item 1", 0
ComboBox1.AddItem "Item 2", 1
'Note: the code below could be removed by setting the corresponding
'design-time properties from the form designer.
ComboBox1.Style = fmStyleDropDownList
Label1.Visible = False
TextBox1.Visible = False
End Sub
Then press F5 to show the form. You'll notice that the label and textbox are only visible when the combo shows "Item 2". The visibility adjustment is performed within the ComboBox1_Change event handler.
If you plan on having numerous controls shown / hidden depending on your combo's value, you could group them into one or more Frame controls, and show / hide those frames instead.

Add event listeners to procedurally generated controls without using a user form

I have a spreadsheet and create ListBox controls in every cell of a column. I'm trying to capture their selected contents but the examples of capturing events on runtime generated controls all involve using a user form and I'm not using one. I'm new to VBA so how can I reproduce the code below from
How to add events to Controls created at runtime in Excel with VBA
Option Explicit
Dim ButArray() As New Class2
Private Sub UserForm_Initialize()
Dim ctlbut As MSForms.CommandButton
Dim butTop As Long, i As Long
'~~> Decide on the .Top for the 1st TextBox
butTop = 30
For i = 1 To 10
Set ctlbut = Me.Controls.Add("Forms.CommandButton.1", "butTest" & i)
'~~> Define the TextBox .Top and the .Left property here
ctlbut.Top = butTop: ctlbut.Left = 50
ctlbut.Caption = Cells(i, 7).Value
'~~> Increment the .Top for the next TextBox
butTop = butTop + 20
ReDim Preserve ButArray(1 To i)
Set ButArray(i).butEvents = ctlbut
Next
End Sub
My code for generating my controls is
Public Sub CreateListbox()
Dim rCell As Range
Dim rRng As Range
Set rRng = ActiveSheet.Range("AA3:AA45")
For Each rCell In rRng.Cells
Set oLISTBOX = ActiveSheet.OLEObjects.Add(classtype:="Forms.ListBox.1")
With oLISTBOX
.Object.IntegralHeight = False
.Object.Font.Size = 11
.Top = rCell.Top
.Left = rCell.Left
.Width = rCell.Width
.Height = rCell.Height
.LinkedCell = rCell.Address
.ListFillRange = "ValSocDeterm."
.Object.ColumnCount = 3
.MultiSelect = 1
End With
Next rCell
End Sub
I basically want to take the example code for creating buttons on a form to creating ListBoxes on a Sheet.
Something like a class module, called clsCustomListBox containing the following code
Option Explicit
Private WithEvents custom As MSForms.ListBox
Public Function initialise(cbConvert As MSForms.ListBox) As Boolean
Set custom = cbConvert
End Function
Private Sub custom_Click()
MsgBox "Clicked"
End Sub
and then a standard module to go through the sheet and get all the listboxes, or you could just add to the collection, when your code adds them.
Option Explicit
Private cls_CustomListBox As clsCustomListbox
Public colCustomListboxCollection As Collection
Public Sub GetListBoxes()
Dim c As OLEObject
Set colCustomListboxCollection = New Collection
For Each c In Worksheets("Sheet1").OLEObjects
If TypeOf c.Object Is MSForms.ListBox Then
Set cls_CustomListBox = New clsCustomListbox
cls_CustomCombo.initialise c.Object
colCustomListboxCollection.Add c
End If
Next c
End Sub
I havent fully tested as at work, but that's where id start.
Hope it helps.

Creating a Userform that changes depending on the elements it should contain

Below is part of the code used to create a chart in Excel:
elements = 0
For j = 1 To x - 1
ans = MsgBox(Activity(j), vbYesNo, "Do you wish for this Element to appear in the Gantt Chart?")
If ans = vbYes Then
elements = elements + 1
ActivityNew(elements) = Activity(j)
End If
Next j
The idea is that I have a list of x-1 activities in the array Activity() only some of which need to be displayed on the chart. These are stored in ActivityNew() and the variable elements counts the population of this array.
At the moment I use a VbYesNo message box to loop through all the activities in Activity() and ask the user to decide which should be shown in the chart.
I would like to show all the activities on a Userform each with a tickbox to either include in ActivityNew() or not but I have no idea how to go about that.
Do you know how to manipulate UserForms ?
You can use this code to create an element :
Set TheTickBox = UserForm.Controls.Add("Forms.TickBox.1", Visible = True)
With TheTickBox
.Left 'Beginning of the tickbox compared to the left side of the UserForm
.Width
.Top 'Beginning of the tickbox compared to the top of the UserForm
.Height
.Caption 'Change the displayed text
End With
So you could use something like this :
For j = 0 to x - 1
Set TheTickBox = UserForm.Controls.Add("Forms.TickBox.1", Visible = True)
With TheTickBox
.Left = 10
.Width = The_Width_You_Want
.Top = 10 + j*The_Height_You_Want
.Height = The_Height_You_Want
.Caption = activity(j)
End With
Next j
At the end of your UserForm you could add a Button 'Validate' that goes through all the tickboxes, and checks the value you've given them:
Sub ButtonValidate_Click()
elements = 0
For each Ctrl in UserForm.Controls
If Ctrl.Value = True Then
ActivityNew(elements) = Ctrl.Caption
elements = elements + 1
End If
Next Ctrl
End Sub
Edit :
To create the UserForm, just click on 'Add UserForm' when right clicking on your project (in the VBA editor).
The code lines I have given you at the beginning will have to be written in the UserForm code box (right click on your UserForm -> Code) and the following in the normal code area :
Sub UserForm()
UserForm.Show 'Here I suppose UserForm is the name of your UserForm
End Sub
And in the UserForm code box make sure that your sub has this name :
Sub UserForm_Initialize()
ACTIONS
End Sub
Please refer to the simplified output below:
To have the output:
1. You need to have user form with its property of list box MulitSelect set to 1.
The following are the codes:
In Userform module:
Private Sub ButtonOK_Click()
Dim Msg As String
Dim i As LoadPictureConstants
Msg = ""
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then _
Msg = Msg & ListBox1.List(i) & vbCrLf
Next i
MsgBox "You selected: " & vbCrLf & Msg
Unload Me
End Sub
Private Sub UserForm_Initialize()
Dim i As Long
Me.Caption = "Select Activities"
Call BuildActivityArray
For i = 1 To 5
Me.ListBox1.AddItem Activity(i)
Next
End Sub
In standard code module:
Sub ShowForm()
UserForm1.Show
End Sub

Loop repeated code to set userform checkbox values

I've a dashboard that uses a userform box field with checkboxes to choose what data to display on a chart.
My code is very copy and pasted.
Private Sub CommandButton21_Click()
UserForm1.Show
If Worksheets("Data Directorate").Range("X4").Value = True Then UserForm1.CheckBox1 = True
If Worksheets("Data Directorate").Range("X5").Value = True Then UserForm1.CheckBox2 = True
If Worksheets("Data Directorate").Range("X6").Value = True Then UserForm1.CheckBox3 = True
Is there a way to use a loop to do this?
I've more repeated code later on:
Private Sub CheckBox1_Click()
Select Case CheckBox1.Value
Case True
Worksheets("Data Directorate").Range("X4").Value = True
Case False
Worksheets("Data Directorate").Range("X4").Value = False
End Select
End Sub
This repeats for 24 check boxes. Would it be possible to loop this?
All great advice posted in this thread, so I'd like to add something that can maybe help to simplify your loops. Controls have a Tag property which, as best I can tell, does nothing other than to store additional information about the control.
Using this to our advantage, we can include the linked cell in the Tag property for each checkbox. (For example, enter X4 into the Tag for linkage to cell X4). This permits for less of the information to be hardcoded, which makes it more adaptable.
Finally, the code would look like this.
Private Sub UserForm_Click()
For Each octrl In Me.Controls
If TypeName(octrl) = "CheckBox" Then
Sheet1.Range(octrl.Tag).Value = octrl.Value
End If
Next octrl
End Sub
My approach in this scenario would be:
Set ControlSource property of the checkboxes to appropriate cells, leave only:
UserForm1.Show
One thing to simplify: instead of using If statements, just make the two sides equal. Like this:
Private Sub CommandButton21_Click()
UserForm1.Show
UserForm1.CheckBox1 = Worksheets("Data Directorate").Range("X4").Value
And the other one:
Private Sub CheckBox1_Click()
Worksheets("Data Directorate").Range("X4").Value = CheckBox1.Value
End Sub
I recommend using the change event instead of the click event. Some user might use Tab and Space, then the click event won't trigger. Like this:
Private Sub CheckBox1_Change()
...
About looping through the checkboxes in the CommandButton21_Click event, that depends on the order of your checkboxes and your table. This might work, but you will have to try it on your table (the order of the checkboxes compared to the order of your cells can ruin the game...)
Dim contr As control
dim i as integer
i=4
For Each contr In UserForm1.Controls
If TypeName(contr) = "CheckBox" Then
contr.Value = cells(i,24).Value 'column 24 is column X
i=i+1
End If
Next
A possible variation to work when CheckBoxes are not in the expected order or get added/removed:
Dim contr As control
Dim J as Integer
Dim Offset as Integer
Offset = 3 'set this to the difference between 1 and the first row containing data
For Each contr In UserForm1.Controls
If TypeName(contr) = "CheckBox" Then
'set j to the checkboxes "number"
J = cInt(right(contr.name, len(contr.name) - len("CheckBox")))
'use the checkbox number + offset to find the row we want
contr.Value = cells(J + Offset,24).Value 'column 24 is column X
End If
Next
Hope this helps.
For the second portion of your question:
Private Sub CheckBox1_Click()
Worksheets("Data Directorate").Range("X4").Value = CheckBox1.Value
End Sub
You can group the checkboxes inside a given frame and try the the following
Sub Test()
Dim i As Long
i = 5
For Each cb In UserForm1.Frame1.Controls
If Worksheets("Data Directorate").Range("X" & i).Value = True Then cb.Value = True
i = i + 1
Next cb
End Sub

VBA - translating code for ActiveX ListBox to forms ListBox

I currently have code used for three ListBoxes in a worksheet (Box1, Box2, Box3), which are all filled with the same values. One box is single select, and the other two are multi select. I am having rendering issues (with the boxes growing in size when other people open the file) and thought it might be worth switching over to forms ListBoxes. I have not yet been able to get the code to translate.
Currently I have this function to populate the boxes:
Sub FillBox(MyBox As MSForms.ListBox, MultiType As Integer, DataArray As Variant)
With MyBox
.Clear
.MultiSelect = MultiType
For j = 1 To UBound(DataArray)
.AddItem DataArray(j)
Next j
End With
End Sub
To glean a value from the single-select box, Box1, I use:
Sheets(2).Cells(1,1) = Box1.Value
To obtain values from Box2 and Box3 this type of code is employed:
For k = 0 To Box2.ListCount - 1
If Box2.Selected(k) = True Then
Sheets(2).Cells(k+1,2) = Box2.List(k)
End If
Next k
I have been unable to get these same functions to work for a forms ListBox, including .Clear, .Selected(), .List(), etc. What is the alternative syntax for these types of operations? Or, if anything, is there a way to avoid bad rendering of ActiveX ListBoxes?
This is the rendering problem that has kicked off this whole thing in the first place:
I had the same problem and here is my solution:
I delete a list box, add it and fill it every time I open a book. So in this case a box will be in a range "B3:D17" every time
Private Sub Workbook_Open()
Set ws = Sheets("Hoja1")
ws.OLEObjects("ListBox1").Delete
Set Rng = ws.Range("B3:D17")
Set mylist = ws.Range("G3:G7")
ws.OLEObjects.Add(ClassType:="Forms.ListBox.1", Link:=False, _
DisplayAsIcon:=False, Left:=Rng.Left, Top:=Rng.Top, _
Width:=Rng.Width, Height:=Rng.Height).Name = "ListBox1"
ws.OLEObjects("ListBox1").ListFillRange = mylist.Address
End Sub
I found out another way, even better:
Resize your listbox on workbook open
Private Sub Workbook_Open()
Set ws = Sheets("Hoja1")
Set Rng = ws.Range("B3:D17")
With ws.Shapes.Range(Array("ListBox1"))
.Left = Rng.Left
.Top = Rng.Top
.Width = Rng.Width
.Height = Rng.Height
End With
End Sub

Resources