I have the following userform:
Using the code below I can save the names of each selected checkbox into a sheet. The value will be saved in the same column, but on different rows (of course), as more than 1 checkbox can be selected.
Dim indProdWs As Worksheet
Dim ctl As Control
Dim i As Long
Set indProdWs = tWb.Worksheets("INDICATION-PRODUCT")
i = 4
' This is the row where i want to save the first value
For Each ctl In seg_multipage.Pages(1).Controls
If TypeName(ctl) = "CheckBox" Then
If ctl.Value = True Then
indProdWs.Cells(i, 9) = ctl.Caption: i = i + 1
End If
End If
Next ctl
However, as you can see in the first image, for each row of products there's a label.
This is what I want to accomplish:
If Product 22 in the second row is selected, then I want the name to be saved on the cell with the following format:
Label2 - Product22
Or if Product 51 in the second row is selected:
Label 5 - Product 51
As you can see, the label number always matches the first digit of the product. I tried using that as a variable, but I haven't been successful.
Thank you for any help you can give me!
is the name actually "Product22" or this just an example? – Siddharth Rout 6 mins ago
#SiddharthRout just an example, but the format is similar. An actual name is seg_cb_selInd_22 (for example). – soraia635 1 min ago
Is this what you are trying?
Change the For - Next code to the below
For Each ctl In seg_multipage.Pages(1).Controls
If TypeName(ctl) = "CheckBox" Then
If ctl.Value = True Then
indProdWs.Cells(i, 9) = Controls("seg_l_selInd_" & _
GetNumber(ctl.Name)).Caption & _
" - " & _
ctl.Name
i = i + 1
End If
End If
Next ctl
and then add this function to your code
Private Function GetNumber(s As String)
Dim numb As Long
Dim i As Long
For i = 1 To Len(s)
Select Case Mid(s, i, 1)
Case 0 To 9
numb = Mid(s, i, 1)
Exit For
End Select
Next
GetNumber = numb
End Function
Note: This function assumes that you will pass an Alphanumeric string to it.
Related
I have a userform in which pages and then on one page contain a lot of comboboxes all of them have a pattern in their name as per their location for example in first row I have 3 comboboxes their names are:
AO11, AO12, AO13
Second row has 04 comboboxes with following names
AO21, AO22, AO23, AO24
Third row has 04 Comboboxes with following names
AO31, AO32, AO33, AO34
and so on till 12~15 Comboboxes
X represents row number when which will start with X=1 because first row will always be visible.
once user press AddNewCMD it should make visible the next row of comboboxes with the following code:
Private Sub AddNewCMD_Click()
If X = 2 Then
Me.AO21.Visible = True
Me.AO22.Visible = True
Me.AO23.Visible = True
Me.AO24.Visible = True
X = X + 1
ElseIf X = 3 Then
Me.AO31.Visible = True
Me.AO32.Visible = True
Me.AO33.Visible = True
Me.AO34.Visible = True
X = X + 1
End If
End Sub
in AO21
2 represents row number
1 represent first combo in row two
however I realize that it will be very long code as if I add further rows in therefore I am search for a way that can loop through each comboboxes and validate x with its name and make them visible?
same goes for hiding the comboboxes.
Please, copy the next code in the userForm code module:
Option Explicit
Private nextRow As Long
Private Sub AddNewCMD_Click()
Dim i As Long, ctrl As MSForms.Control, boolVis As Boolean
If nextRow = 0 Then
nextRow = 2
Else
nextRow = nextRow + 1
End If
Debug.Print nextRow
For i = 0 To Me.MultiPage1.Pages(0).Controls.count - 1
Set ctrl = Me.MultiPage1.Pages(0).Controls(i)
If TypeOf ctrl Is MSForms.ComboBox Then
If CLng(Right(ctrl.Name, Len(ctrl.Name) - 2)) > 13 Then
Debug.Print ctrl.Name, ctrl.Visible
If CLng(Mid(ctrl.Name, 3, 1)) = nextRow Then
ctrl.Visible = True: boolVis = True
End If
End If
End If
Next i
If Not boolVis Then MsgBox "The row " & nextRow & " does not exist..."
End Sub
ClickingAddNewCMD button will make the next combos row visible. When the existing rows are all made visible, a message stating that no any row available will be raised.
You did not answer my clarification questions... But if only one such combo row should be visible (except the first one, or including), I can make a function to determine it, but this can be done only if you supply a logic algorithm based on what to proceed.
I have a big list with full addresses in excel. Each address has a single cell. I am having trouble creating a formula to grab the street name to put in another cell and grabbing the city to put in a different cell.
Here is example cases of what my data looks like
12 Apple RD Harwich, MA 11111
1213 Strawberry Crossing Loop Tampa, FL 22222
123 Pear Dr. Colorado Springs, CO 33333
12345 RIVERSIDE DR Lowertown, PA 44444
6232 N Rockstar ST Philadelphia, PA 44444
123 TOWN ST Plympton, MA 55555
I didn't find a quick and easy way to solve your problem but here is a way to do the required work efficiently and fast - probably more so than if you spend time on getting code that will, at best, only produce a result that needs to be reviewed.
In a nutshell, the code offered here will create a textbox for each cell as you click on it. It's a textbox because it offers capabilities a cell doesn't have. In the text box you enter a single comma, to separate street address from city, and press enter. The split is done on the spot, is immediately editable, and the textbox moves to the next line.
The code can handle more commas. And it can handle each part individually. I have demonstrated this on the state and ZIP code part. There is a note in the code where you can remove this extra. The code also adds the comma (and any other changes made at that time) to the original data. There is another note in the code where you can remove a line to keep the original data untouched.
The code is a little elaborate and, at the same time, rough around the edges because it was adapted from another project. As it is now in consists of 2 parts. The first part contains event procedures which call other procedures in the other part. The latter also contains supporting functions for itself. The first part must be installed in the code module of the worksheet on which you want the action. That is the worksheet with the original addresses in them. You can install this same code behind several worksheets in the same workbook. Here is part 1.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' 069
Const StopAction As Boolean = False ' change to TRUE to stop
Const SourceClm As Long = 1 ' column containing the data
If StopAction Or Target.Column <> SourceClm Then
KillTbx Target
Else
SetTbx Target.Cells(1)
End If
End Sub
Private Sub Splitter_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
' NIC 047 09 Jun 2020
KeyCode = KeyUpEvent(KeyCode, Shift)
End Sub
Please observe the two constants at the top. You can set them to suit your needs. StopAction, if TRUE will disable the creation of text boxes, in fact returning your worksheet to its original behaviour. SourceClm specifies the column in which you have your original data. In my trials that was column A, identified by its number, 1. If you have several installations in the same workbook these settings can be individually different.
The code below goes into a standard code module. That is a module you have to insert. By default VBA will name it Module1. I recommend to rename it suitably. I named mine STO_62962096 which will help me find this thread again if ever needed.
Option Explicit
Private Const MagName As String = "Splitter"
Sub SetTbx(Target As Range)
' 069
Dim Tbx As OLEObject ' the TextBox being created
Dim BackColor As Long ' background color
Dim FontColor As Long ' font color
BackColor = 16777152 ' = sky blue
FontColor = vbBlack ' = 0
On Error Resume Next
Set Tbx = ActiveSheet.OLEObjects(MagName)
If Err Then
Set Tbx = Target.Worksheet.OLEObjects _
.Add(ClassType:="Forms.TextBox.1", _
Link:=False, _
DisplayAsIcon:=False, _
Left:=100, Top:=100, _
Width:=100, Height:=20)
End If
With Tbx
With .Object
.BackColor = BackColor
.SpecialEffect = fmSpecialEffectFlat
.BorderStyle = fmBorderStyleSingle
.IntegralHeight = False
.ForeColor = FontColor
.Font.Size = Target.Font.Size
.Text = Target.Value
End With
.Left = Target.Left
.Top = Target.Top
.Width = Target.Width
.Height = (Target.Offset(1).Top - .Top)
.Name = MagName
.Activate
End With
End Sub
Sub KillTbx(Target As Range)
' 069
Dim Tbx As OLEObject ' TextBox
On Error Resume Next
Set Tbx = Target.Worksheet.OLEObjects(MagName)
If Err = 0 Then Tbx.Delete
Err.Clear
Target.Select
End Sub
Function KeyUpEvent(ByVal KeyCode As Integer, _
ByVal Shift As Integer) As Integer
' 069
Dim Tbx As OLEObject
Dim n As Long ' offset
Set Tbx = ActiveSheet.OLEObjects(MagName)
If KeyCode = 13 Then ' Enter
With Tbx
SplitAddress .Object.Text, .TopLeftCell.Row
' remove the next line to KEEP original data
.TopLeftCell.Value = .Object.Text
KeyCode = 40 ' move to next row
End With
End If
Select Case KeyCode
Case 38, 40 ' Up-arrow / Down-arrow
n = IIf(KeyCode = 38, -1, 1)
Tbx.TopLeftCell.Offset(n).Select
Tbx.Object.Text = ActiveCell.Value
Case 9 ' tab: move right/left
n = IIf(Shift, -1, 1)
Tbx.TopLeftCell.Offset(, n).Select
Tbx.Object.Text = ActiveCell.Value
End Select
KeyUpEvent = KeyCode
End Function
Private Sub SplitAddress(ByVal Txt As String, _
ByVal Rt As Long)
' 069
Const TgtClm As Long = 4 ' first target column (change to suit)
Const StateClm As Long = 7 ' State followed by ZIP (change to suit)
Dim Sp() As String ' address array
Dim Ct As Long ' target column
Dim Arr As Variant ' output array
If Len(Txt) Then
ReDim Arr(1 To StateClm - TgtClm + 2)
Sp = Split(Txt, ",")
For Ct = 0 To UBound(Sp)
Arr(Ct + 1) = Trim(Sp(Ct))
Next Ct
' remove the next block of 5 lines to NOT separate state & ZIP
Sp = Split(Trim(Replace(Sp(Ct - 1), " ", " ")))
Arr(Ct) = ""
For Ct = 0 To UBound(Sp)
Arr(Ct + StateClm - TgtClm + 1) = Trim(Sp(Ct))
Next Ct
Cells(Rt, TgtClm).Resize(, UBound(Arr)).Value = Arr
Columns(TgtClm).Resize(, StateClm - TgtClm + 2).AutoFit
End If
End Sub
Look for the procedure SplitAddress and adjust the two constants you find there. The code splits the address into a, theoretically, unlimited number of parts. The first of these will be written to the column named TgtClm, 4 in the above code, which identifies column D. The State/ZIP combination has its own similar design and therefore its own first column (the first of 2 in this case). If you don't use this feature (you can disable it in this same procedure) set the constant StateClm to a number at least equal to the maximum number of splits you expect.
Note that the code creates an array with StateClm - TgtClm + 2 elements. If you only want 3 columns, as per your question, StateClm - TgtClm + 2 must be => 3. To the right of the result the code will over-write existing data for as many columns as this formula specifies.
I have a Userform that will be used to complete audits on employee work with buttons for response Yes, No and N/A. There are corresponding Textboxes for each button. There are 2 sections that are being audited and what I need is to ensure that only one of the Textboxes in each section is filled in - They either Passed, Failed or the section did not apply.
My Textbox names are:
TXTPaymentPass
TXTPaymentFail
TXTPaymentNA
TXTReservePass
TXTReserveFail
TXTReserveNA
The button code is simply(One for each button):
Private Sub BTNFPayment_Click()
TXTPaymentFail = "No"
End Sub
The code for the calculation step is(it calculates everything but more than one text box can be filled in currently):
Private Sub CommandButton1_Click()
Dim c As Control, nYes As Long, nNo As Long, nNA As Long
nYes = 0
nNo = 0
nNA = 0
For Each c In Me.Controls
If TypeName(c) = "TextBox" Then
If c.Value = "Yes" Then nYes = nYes + 1
If c.Value = "No" Then nNo = nNo + 1
If c.Value = "NA" Then nNA = nNA + 1
End If
Next c
TXTScore = Format((nYes) / (nYes + nNo + nNA), "Percent")
For Each c In Me.Controls
If TypeName(c) = "Textbox" Then
If c.Value = "" Then na = MsgBox("Please choose Yes, No, or N/A before continuing.", vbOKOnly, "Quarterly Audits")
End If
Next c
End Sub
Right now I have a bit of code that is checking to make sure that none of the textboxes are blank. Instead, I need to make sure that only One textbox is filled in, in each section (Payment/Reserve) but also that at least one is filled in (form cant be completely blank).
Here is my loop that checks which boxes have true values and assigns their tag names into a string (fund1, fund2, fund3):
counter = 0
Dim ctrl As MSForms.Control
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" Then
If ctrl.Value = True Then
counter = counter + 1
If counter = 1 Then
fund1 = ctrl.Tag
ElseIf counter = 2 Then
fund2 = ctrl.Tag
ElseIf counter = 3 Then
fund3 = ctrl.Tag
End If
End If
End If
Next ctrl
The code works fine however say i have 5 check boxes with tags and names a through e. I want to set an order of importance for which checkboxes get looped through first. Currently, for example:
fund 1 = c.tag
fund 2 = a.tag
fund 3 = b.tag
But I would want:
fund 1 = d.tag
fund 2 = b.tag
fund 3 = c.tag
How to I prioritize certain check boxes to be looped through first?
I tried changing the TabIndexes in each checkbox's properties and was unsuccessful.
Thanks in advance
If you are talking about a priority across checkboxes, I guess you already know them and you don't discover them at run-time.
In that case, just create your own collection (which you sort the way you want), declare it on top of the module (so that the variable remains global and accessible all over the execution of your program) and populate it once at initialization.
For example:
Public prioritizedCheckboxes As Collection '<-- on top of the form module (global variable)
'--------------------------------------------
Private Sub initializePrioritizedList()
Set prioritizedCheckboxes = New Collection
With prioritizedCheckboxes
.Add d '<-- most important
.Add c '<-- second most important
'...
.Add b '<-- less important
End With
End Sub
'--------------------------------------------
Private Sub UserForm_Initialize()
initializePrioritizedList '<-- initialize your list when you initialize the form (or whenever you prefer)
End Sub
'--------------------------------------------
... and then use your code with the priorized collection:
counter = 0
Dim ctrl As MSForms.Control
For Each ctrl In prioritizedCheckboxes '<-- you sorted the list. So you know first you'll have d, then c, then a etc.
If ctrl.Value = True Then
counter = counter + 1
If counter = 1 Then
fund1 = ctrl.Tag
ElseIf counter = 2 Then
fund2 = ctrl.Tag
ElseIf counter = 3 Then
fund3 = ctrl.Tag
End If
End If
Next ctrl
You could name your check boxes to reflect their priority (e.g.: "myCB1", "myCB2", etc...) and the directly get them via Controls collection:
Dim iCB As Long, counter As Long
Dim fund1 As String, fund2 As String, fund3 As String
With Me
For iCB = 1 To 5
With .Controls("myCB" & iCB)
If .Value Then
counter = counter + 1
If counter = 1 Then
fund1 = .Tag
ElseIf counter = 2 Then
fund2 = .Tag
ElseIf counter = 3 Then
fund3 = .Tag
End If
End If
End With
Next
End With
Moreover, a Select Case structure could be more readable:
Dim iCB As Long, counter As Long
Dim fund1 As String, fund2 As String, fund3 As String
With Me
For iCB = 1 To 5
With .Controls("myCB" & iCB)
If .Value Then
counter = counter + 1
Select Case counter
Case 1
fund1 = .Tag
Case 2
fund2 = .Tag
Case 3
fund3 = .Tag
End Select
End If
End With
Next
End With
Finally you could consider using an array instead of three variables:
Dim iCB As Long, counter As Long
Dim funds(1 To 3) As String
With Me
For iCB = 1 To 3
With .Controls("myCB" & iCB)
If .Value Then
counter = counter + 1
funds(counter) = .Tag
End If
End With
Next
End With
and then access them with funds(1), funds(2) and funds(3)
I’m using a userform with 12 listboxes (numbered 2-13). Each list box could contain 0-8 items assigned by user from main listbox1. I run the following code to output the content of each list box (12 boxes) to sheet “Tray” when a button is pressed.
Each listbox is then output into corresponding columns of each tray from columns B-M. Listbox2 fills column 1 of each tray and so on. A maximum of 4 trays can be filled. The code checks the 1st well of each tray and if it contains a value it assumes the tray is full & begins filling the next tray.
Problem: If the first tray contains a blank column(listbox) and the second tray contains values in the same listbox, the code will fill blank column of the frist tray with values that should be in the second tray. Please see pictures below and updated code below:
Listboxes 2,3 and 4 for Tray 1 (note listbox3 is empty)
Listboxes 2,3 and 4 for tray 2 (note listbox3 has data)
Code ran two times: Listbox3 from tray2 appears in tray1 (erroneously!!!)
Expected output:
Sub Worklist()
'
Dim Var, VarName As Variant
Dim i, DblDashPos, FirstPeriodPos, lngColNum, lngRowNum As Long
Dim item As ListBox
Const cstrNames As String = "Listbox2,Listbox3,Listbox4,Listbox5,Listbox6,Listbox7,Listbox8,Listbox9,Listbox10,Listbox11,Listbox12,Listbox13"
Application.ScreenUpdating = False
lngColNum = 2
For Each VarName In Split(cstrNames, ",")
If UserForm2.Controls(VarName).ListIndex <> -1 Then 'if listbox is not blank
If Sheets("Tray").Cells(4, lngColNum).Value = 0 Then
'checks if value in row 3 column "lngColNum" is empty
lngRowNum = 4
ThisWorkbook.Sheets("Tray").Range("C2").Value = UserForm2.TextBox1.Value
ElseIf Sheets("Tray").Cells(15, lngColNum).Value = 0 Then 'checks if value in row 14 column "lngColNum" is empty
lngRowNum = 15
ThisWorkbook.Sheets("Tray").Range("C13").Value = UserForm2.TextBox1.Value
ElseIf Sheets("Tray").Cells(26, lngColNum).Value = 0 Then 'checks if value in row 14 column "lngColNum" is empty
lngRowNum = 26
ThisWorkbook.Sheets("Tray").Range("C24").Value = UserForm2.TextBox1.Value
Else 'otherwise assumes tray starts in row 5, column "lngColNum"
lngRowNum = 37
ThisWorkbook.Sheets("Tray").Range("C35").Value = UserForm2.TextBox1.Value
End If
For i = 0 To UserForm2.Controls(VarName).ListCount - 1
Var = UserForm2.Controls(VarName).List(i)
DblDashPos = InStr(1, Var, "--")
FirstPeriodPos = InStr(1, Var, ".")
Sheets("Tray").Select
ActiveSheet.Cells(lngRowNum, lngColNum) = Left(Var, DblDashPos - 1) & Right(Var, Len(Var) - FirstPeriodPos + 1)
lngRowNum = lngRowNum + 1
Next i
End If
lngColNum = lngColNum + 1
Next
Application.ScreenUpdating = True
End Sub
Thank you very much!
The problem is that you're only testing the column that corresponds to the ListBox to see if the cell is empty. If you want to test that all of the columns in a "tray" are empty, you need to test once for the entire sheet. Something like this (untested because I'm too lazy to rebuild your form):
Private Function FindFirstUnusedRow(sheet As Worksheet) As Long
Dim testColumn As Long, testRow As Long
Dim used As Boolean
For testRow = 4 To 37 Step 11
used = False
For testColumn = 2 To 13
If IsEmpty(sheet.Cells(testRow, testColumn)) = False Then
used = True
Exit For
End If
Next testColumn
If used = False Then
FindFirstUnusedRow = testRow
Exit For
End If
Next testRow
End Function
Then in your code, call it before your loop:
Sub Worklist()
Dim var As Variant
Dim i As Long, dashPos As Long, periodPos As Long, colNum As Long
Dim rowNum As Long, Dim sheet As Worksheet
Application.ScreenUpdating = False
Set sheet = ThisWorkbook.Sheets("Tray")
rowNum = FindFirstUnusedRow(sheet)
If rowNum = 0 Then
Debug.Print "All trays full."
Exit Sub
End If
Dim current As ListBox
For colNum = 2 To 13
Set current = UserForm2.Controls("Listbox" & colNum)
If current.ListIndex <> -1 Then 'if listbox is not blank
sheet.Cells(rowNum - 2, colNum).Value = UserForm2.TextBox1.Value
For i = 0 To current.ListCount - 1
var = current.List(i)
dashPos = InStr(1, var, "--")
periodPos = InStr(1, var, ".")
sheet.Cells(rowNum + i, colNum) = Left$(var, dashPos - 1) & _
Right$(var, Len(var) - periodPos + 1)
Next i
End If
Next colNum
Application.ScreenUpdating = True
End Sub
A couple other notes: You can ditch the Sheets("Tray").Select line entirely - you never use the selection object. Same thing with the mixed references to ActiveSheet and ThisWorkbook.Sheets("Tray"). Grab a reference and use it.
Also, these lines don't do what you think they do:
Dim Var, VarName As Variant
Dim i, DblDashPos, FirstPeriodPos, lngColNum, lngRowNum As Long
Of all the variables you declare, everything is a Variant except lngRowNum. If you want to combine declarations on one line like that, you still need to specify a type for each variable, or they'll default to Variant. See the example code above.