VBA Excel - Simplifiying For To function IF statements - vba

I have a Combo Box (GSMListType) with different variables "A", "A - K", "B", "C", etc... linked to a List Box (AvailableNumberList) which calls on cells from different sheets based on the selection in the Combo Box.
The Combo Box has 32 different variables which call on 32 different sheets. Below is a sample of the code.
"A" collects the data in sheet A_Regular and adds its items into the List Box
"A - K" collects the data in sheet A_K and adds its items into the List Box
"B" collects the data in sheet B_Regular and adds its items into the List Box
"C" collects the data in sheet C_Regular and adds its items into the List Box
and so on...
Is there a way to simplify the below mentioned code? The sheet is fully functional, but the code is a mess.
Private Sub GSMListType_Change()
Dim TypeLookup As Double
'If listing has changed, clear AvailableNumberList and insert new data
If GSMListType.ListIndex > -1 Then
AvailableNumberList.Clear
If GSMListType.Value = "A" Then
TypeLookup = Application.WorksheetFunction.CountIf(A_Regular.Range("A:E"), GSMListType.Value)
With AvailableNumberList
For k = 2 To TypeLookup + 1
.AddItem A_Regular.Range("A" & k).Value
Next k
End With
ElseIf GSMListType.Value = "A - K" Then
TypeLookup = Application.WorksheetFunction.CountIf(A_K.Range("A:E"), GSMListType.Value)
With AvailableNumberList
For k = 2 To TypeLookup + 1
.AddItem A_K.Range("A" & k).Value
Next k
End With
ElseIf GSMListType.Value = "B" Then
TypeLookup = Application.WorksheetFunction.CountIf(B_Regular.Range("A:E"), GSMListType.Value)
With AvailableNumberList
For k = 2 To TypeLookup + 1
.AddItem B_Regular.Range("A" & k).Value
Next k
End With
ElseIf GSMListType.Value = "C" Then
TypeLookup = Application.WorksheetFunction.CountIf(C_Regular.Range("A:E"), GSMListType.Value)
With AvailableNumberList
For k = 2 To TypeLookup + 1
.AddItem C_Regular.Range("A" & k).Value
Next k
.
.
.
End With
End If
End If
End Sub

I don't think this improves your original code sample by any substantial measure but it does tidy things up by reducing the repetitious sections.
Private Sub GSMListType_Change()
Dim TypeLookup As Long, ws As Worksheet
'If listing has changed, clear AvailableNumberList and insert new data
If GSMListType.ListIndex > -1 Then
With GSMListType
Select Case .Value
Case "A"
Set ws = A_Regular 'Sheets("A_Regular") ?????
Case "A - K"
Set ws = A_K
Case "B"
Set ws = B_Regular
Case "C"
Set ws = C_Regular
Case Else
'do nothing
End Select
TypeLookup = Application.CountIf(ws.Range("A:E"), .Value)
End With
With AvailableNumberList
.Clear
For k = 2 To TypeLookup + 1
.AddItem ws.Range("A" & k).Value
Next k
End With
End If
Set ws = Nothing
End Sub
I wasn't sure about your worksheet designation method to some module-wide variable pointing to the various worksheets so I includes a commented alternative that uses the worksheet name(s).

Related

Nested Loop Excel VBA

I need your help in nexted VBA loop. I have some data in two columns and blank rows between rows. This macro loop through a column and find out if it contain certain character. If it' blank then I want it to move to next row. If it contain "Den", then select a specific worksheet ("D-Temp") else select ("M-Temp").
After selecting right Worksheet, it need to fill up text boxs with data from 2nd column as per Row no. The code I have created so far is
Sub Template()
Dim j As Long
Dim c As Range, t As Range
Dim ws As String
j = 5
With Sheets("Sample ")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp))
If c.Value = "" Then
Next ' `Not getting how to jump to next one`
ElseIf c.Value = "DEN" Then
ws = "D-Temp"
Else
ws = "M-Temp"
End If
For Each t In .Range("P3", .Cells(.Rows.Count, "P").End(xlUp))
If t.Value <> "" Then
j = j + 1
Sheets("M-Temp").Copy after:=Sheets(Sheets.Count)
ActiveSheet.Shapes("Textbox 1").TextFrame.Characters.Text = t.Value
ActiveSheet.Shapes("textbox 2").TextFrame.Characters.Text = t.Offset(, -1).Value
End If
Next
Next
End With
Any help ??
Below is the sample Data I have :
Type Name 1 Name2
DEN Suyi Nick
'Blank row'
PX Mac Cruise
I want macro to Identify Type & select template worksheet (D or M) as per that and fill textboxes on that template with Name 1 & Name2 respectively.
may be you're after this:
Option Explicit
Sub Template()
Dim c As Range
With Sheets("Sample")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp)).SpecialCells(xlCellTypeConstants) ' loop through referenced sheet column C not empty cells form row 3 down to last not empty one
Worksheets(IIf(c.Value = "DEN", "D-Temp", "M-Temp")).Copy after:=Sheets(Sheets.Count) ' create copy of proper template: it'll be the currently "active" sheet
With ActiveSheet ' reference currently "active" sheet
.Shapes("Textbox 1").TextFrame.Characters.Text = c.Offset(, 7).Value ' fill referenced sheet "TextBox 1" shape text with current cell (i.e. 'c') offset 7 columns (i.e. column "P") value
.Shapes("Textbox 2").TextFrame.Characters.Text = c.Offset(, 6).Value ' fill referenced sheet "TextBox 2" shape text with current cell (i.e. 'c') offset 6 columns (i.e. column "O") value
End With
Next
End With
End Sub
If I'm not mis-understanding your current nesting...
With Sheets("Sample ")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp))
If c.Value <> "" Then
If c.Value = "DEN" Then
ws = "D-Temp"
Else
ws = "M-Temp"
End If
For Each t In .Range("P3", .Cells(.Rows.Count, "P").End(xlUp))
If t.Value <> "" Then
j = j + 1
Sheets("M-Temp").Copy after:=Sheets(Sheets.Count)
ActiveSheet.Shapes("Textbox 1").TextFrame.Characters.Text = t.Value
ActiveSheet.Shapes("textbox 2").TextFrame.Characters.Text = t.Offset(, -1).Value
End If
Next
End if 'not blank
Next
End With
If I understand your question, correctly, you need to change your if/then logic slightly:
Sub Template()
Dim j As Long
Dim c As Range, t As Range
Dim ws As String
j = 5
With Sheets("Sample ")
For Each c In .Range("I3", .Cells(.Rows.Count, "I").End(xlUp))
If c.Value <> "" Then
If c.Value = "DEN" Then
ws = "D-Temp"
Exit For
Else
ws = "M-Temp"
Exit For
End If
End If
Next
For Each t In .Range("P3", .Cells(.Rows.Count, "P").End(xlUp))
If t.Value <> "" Then
j = j + 1
Sheets("M-Temp").Copy after:=Sheets(Sheets.Count)
ActiveSheet.Shapes("Textbox 1").TextFrame.Characters.Text = t.Value
ActiveSheet.Shapes("textbox 2").TextFrame.Characters.Text = t.Offset(, -1).Value
End If
Next
End With
End Sub
You might want to add code to make sure that ws is set to something (not all columns were blank).

Iterations on a userform

I have a userform that I need to get users to enter multiple sets of tasks, as well as an estimate of the time that it will take to undertake each task.
After each task, and time estimate, I would like for the information to be entered on a spreadsheet, and the fields become blank again, for the next task to be entered.
This is the main code:
Global i As Integer ' Rows
Global j As Integer ' Columns
Global tCount As Integer ' Task Count
Sub Time_Calcs()
Dim mcHours As Integer ' M/C process in hours
Dim hDays As Integer ' Hours available per day
i = 2
j = 3
tCount = 1
hDays = 6
Worksheets("Calculations").Activate
Cells.Delete
i = i + 1
Cells(i, 2) = "Item"
Cells(i, 3) = "Task"
Cells(i, 4) = "# of iterations"
Cells(i, 5) = "Maker"
Cells(i, 6) = "Checker"
i = i + 1
TaskForm.Show
End Sub
I have 2 questions:
1) How can I get the code to loop through a series of instructions, such as taking the information from the form, and putting it into a spreadsheet, before clearing the data for the next task to be entered? I've tried this code, but it only seems to work for one iteration.
Private Sub CommandButton1_Click()
j = 3
Cells(i, 2) = tCount
While j <= 6
If j = 3 Then
Cells(i, j) = TaskName
ElseIf j = 4 Then
Cells(i, j) = NoofIts
ElseIf j = 5 Then
Cells(i, j) = mTime
ElseIf j = 6 Then
Cells(i, j) = cTime
End If
j = j + 1
Wend
i = i + 1
j = 3
tCount = tCount + 1
'MSForms.Control(TaskName).Value = vbNullString
'MSForms.Control(NoofIts).Value = vbnullstrins
'MSForms.Control(mTime).Value = vbNullString
'MSForms.Control(cTime).Value = vbNullString
TaskName = vbNullString
NoofIts = vbNullString
mTime = vbNullString
cTime = vbNullString
End Sub
2) After I enter data, I would like the user to be able to TAB to the next box. Currently, if I hit TAB, it TABs the cursor right. How do I get it to enable moving to the next box/button via the TAB button?
You should have a couple of procedures to add the data to the worksheet and to clear the form of existing data.
Clearing the form is just a case of going through each control on the form and settings it's value to some default - usually Null.
Private Sub Reset()
Dim ctrl As Control
For Each ctrl In Me.Controls
Select Case TypeName(ctrl)
Case "TextBox", "ComboBox"
ctrl.Value = Null
Case "OptionButton"
ctrl.Value = False
Case Else
'Do nothing
End Select
Next ctrl
End Sub
Saving the form data can be quite complicated depending on the checks you want to make before allowing the data to be transferred.
I make use of the Tag property of a control to store the column number is should be saved in and the data type of the data.
So something like 16;CCur would indicate it will be saved in column 16 as currency.
The actual code to save the data would start with finding the last cell on the worksheet containing data - this can then be used to place the new data on the next available row.
Each control is then checked and the data saved to the column indicated by in the tag property.
After all the data has been saved the form is reset and the initialize routine executed
Private Sub btnSave_Click()
Dim rLastCell As Range
Dim ctrl As Control
Dim lCol As Long
Dim wrkSht As Worksheet
Set wrkSht = ThisWorkbook.Worksheets("Raw Data")
Set rLastCell = wrkSht.Cells(wrkSht.Rows.Count, 1).End(xlUp).Offset(1)
For Each ctrl In Me.Controls
With ctrl
If TypeName(ctrl) = "TextBox" Or TypeName(ctrl) = "ComboBox" Then
If Trim(ctrl.Value) <> "" Then
If InStr(.Tag, ";") > 0 Then
lCol = Split(.Tag, ";")(0)
'Decide which data type to use.
Select Case Split(.Tag, ";")(1)
Case "CLNG"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CLng(ctrl.Value)
Case "CCur"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CCur(ctrl.Value)
Case "CDATE"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CDate(ctrl.Value)
Case "CSTR", "CSENTENCE"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CStr(ctrl.Value)
Case "CDBL"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CDbl(ctrl.Value)
Case "CPER"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CDbl(ctrl.Value) / 100
wrkSht.Cells(rLastCell.Row, CLng(lCol)).NumberFormat = "0.00%"
Case Else
End Select
End If
End If
End If
End With
Next ctrl
Reset
UserForm_Initialize
End Sub
I've added the UserForm_Initlialize procedure as it sets up the form for data entry - todays date is entered in a control, combo-boxes are set up, a label is given a caption showing the current Value Added Tax amount from a named range and the correct control is given focus:
Private Sub UserForm_Initialize()
Me.TextBox1 = Format(Date, "dd-mmm-yyyy")
Me.lblVAT = "VAT # " & Format$(ThisWorkbook.Names("VAT").RefersToRange, "Percent")
With Me.ComboBox1
.AddItem "A"
.AddItem "B"
.AddItem "C"
End With
Me.TextBox1.SetFocus
End Sub
I have extensions to the code - code that automatically converts names to propercase, doesn't allow more than 2 decimal places or only allows whole numbers. There's also code to check that required data has been entered and highlight the controls which are missing data before saving to the sheet. That would take a whole lot more to explain though.

Userform lockups using loops not working

I am trying to set up my user form to do a loop or look up to reference my table which is on a sheet and is a large data base.
I want my user form to look up what I type and then auto fill in the other textboxes so that I can limit the number of duplicates and make it more stream lined.
My code is as shown below is embedded into Textbox1 and is set up to run the code after change. It is still not working and I have worked for many days and weeks trying to figure this out.
Option Explicit
Dim id As String, i As String, j As Integer, flag As Boolean
Sub GetDataA()
If Not IsNumeric(UserForm1.TextBox1.Value) Then
flag = False
i = 0
id = UserForm1.TextBox1.Value
Do While Cells(i + 1, 1).Value <> ""
If Cells(i + 1, 1).Value = id Then
flag = True
For j = 2 To 7
UserForm1.Controls("TextBox" & j).Value = Cells(i + 1, j).Value
Next j
End If
i = i + 1
Loop
If flag = False Then
For j = 5 To 10
UserForm1.Controls("TextBox" & j).Value = ""
Next j
End If
Else
End If
End Sub
you may want to adopt this refactoring of your code
Option Explicit
Sub GetDataA()
Dim j As Integer
Dim f As Range
With UserForm1 '<--| reference your userform
If Not IsNumeric(.TextBox1.Value) Then Exit Sub '<--| exit sub if its TextBox1 value is not a "numeric" one
Set f = Range("A1", Cells(Rows.Count, 1).End(xlUp)).Find(what:=.TextBox1.Value, LookIn:=xlValues, lookat:=xlWhole) '<--| try and find its TextBox1 value along column "A" cells from row 1 down to last not empty one
If f Is Nothing Then '<--| if not found
For j = 5 To 10
.Controls("TextBox" & j).Value = ""
Next j
Else '<--| if found
For j = 2 To 7
.Controls("TextBox" & j).Value = f.Offset(, j - 1).Value
Next j
End If
End With
End Sub
note: if this sub is actually inside UserForm1 code pane than you can change With UserForm1 to With Me

Changing text inside of shapes

I'm looking to search a sheet with shapes containing text that match the "A" column and then to change it to the corresponding "B" column. I wish to do so with multiple shapes. I have linked to a simplified version of the sheet
I will be working on:
Sub linkCell()
For i = 1 To 3
For j = 1 To 3
If ActiveSheet.Shapes(i).Value = ActiveSheet.Range("A" & j).Value Then
ActiveSheet.Shapes(i).Value = "=B" & j
End If
Next j
Next i
End Sub
This is faithful to your vision:
Sub linkCell()
Dim s As Shape, r As Range
For Each r In [a1:a3]
For Each s In ActiveSheet.Shapes
If r = s.TextFrame.Characters.Caption Then
s.OLEFormat.Object.Formula = "=" & r(, 2).Address
Exit For
End If
Next
Next
End Sub

Dynamic combobox based on row from different sheet

I've got 2 sheets in my workbook in Excel 2010. The first got a row with names, listed like this:
Jens A.
Christian
Peter
Jens A.
Anders
Jens A. etc.
On the second sheet I want to make a combobox, which show all the names from the first sheet, but without duplicates. Is that possible to make in VBA code?
In addition I want to make the list dynamic, but I guess I've just have to call the function in:
Private Sub Workbook_Open()
To make that happen?
Thanx in advance
Plese check the below macro. It operates on 2 columns but you can adjust it.
Option Explicit
Sub UniqueRecords()
Dim i As Long
Dim j As Long
Dim k As Long
Dim bDuplicate As Boolean
i = 1
Do Until Cells(i, 1).Value = ""
'check if record exist
j = 1
Do Until Cells(j, 2).Value = ""
bDuplicate = False
If Cells(i, 1).Value = Cells(j, 2).Value Then
bDuplicate = True
Exit Do
End If
j = j + 1
Loop
'add record if no duplicate
If bDuplicate = False Then
For k = 1 To ActiveSheet.Rows.Count
If Cells(k, 2).Value = "" Then
Cells(k, 2).Value = Cells(i, 1).Value
Exit For
End If
Next k
End If
i = i + 1
Loop
End Sub