Find Roll and Column of Name with latest Date - vba

I have a table with three columns,
ID, Name and Date
then I create a userform with textbox ID and Name.
how could I display the Name of similar ID from the table with latest Date when I key in the ID in the userform? (similar ID will have different names, but I want to display the one with latest date in the table)
thanks in advance for all the help
coding for the textbox1
Private Sub TextBox1_Change()
getdata
End Sub
coding for the getdata module
Sub getdata()
If 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 3
UserForm1.Controls("textbox" & j).Value = Cells(i + 1, j).Value
Next j
End If
i = i + 1
Loop
If flag = False Then
For j = 2 To 3
UserForm1.Controls("TextBox" & j).Value = ""
Next j
End If
Else
ClearForm
End If
End Sub

This should do it for you. This routine goes in your userform code module:
Private Sub TextBox1_AfterUpdate()
TextBox2 = Evaluate("=INDEX(B2:B999,MATCH(MAX((IF(A2:A999=" & TextBox1 & _
",1)*(C2:C999)),1),IF(A2:A999=" & TextBox1 & _
",1)*(C2:C999),))")
End Sub
It assumes your data are in columns A, B, and C. It also assumes your data do not extend past row 999; if they do, then increase the 999's in the formula to what is appropriate.
TextBox1 is for the ID.
TextBox2 is for the Name.
Note that this code is placed in the AfterUpdate event procedure. This is different than your sample code. You used the Change event procedure. The difference is that Change fires on each keystroke while AfterUpdate fires only after the full text is confirmed for the textbox.
Note that you should still add error checking for the case where the ID is not numeric and also for the case where the numeric ID does not match. The code above is simply for demonstrating the technique to display the looked-up value. If you wish for me to flesh it out more, please let me know.
UPDATE
I went ahead and fleshed it out with the error checking:
Private Sub TextBox1_AfterUpdate()
GetData
End Sub
Public Sub GetData()
Dim v, w
On Error Resume Next
v = Evaluate("=INDEX(B2:B999,MATCH(MAX((IF(A2:A999=" & TextBox1 & _
",1)*(C2:C999)),1),IF(A2:A999=" & TextBox1 & _
",1)*(C2:C999),))")
w = Evaluate("MAX((IF(A2:A999=" & TextBox1 & ",1)*(C2:C999)))")
If IsArray(v) Or IsError(v) Then v = "ID not found.": w = ""
TextBox2 = v
TextBox3 = "": TextBox3 = CDate(w)
End Sub
UPDATE 2
In the fleshed out version directly above, I added support for the associated date in TextBox3.

You could read the whole range in when the userform opens, sort it, then find the first ID.
Private mvaData As Variant
Private Sub TextBox1_AfterUpdate()
Me.TextBox2.Text = vbNullString
Me.TextBox3.Text = vbNullString
GetData
End Sub
Public Sub GetData()
Dim i As Long
For i = LBound(mvaData, 1) To UBound(mvaData, 1)
If mvaData(i, 1) = Val(Me.TextBox1.Text) Then
Me.TextBox2.Text = mvaData(i, 2)
Me.TextBox3.Text = mvaData(i, 3)
Exit For 'stop after the first one - largest date
End If
Next i
End Sub
Private Sub UserForm_Initialize()
Dim i As Long, j As Long
Dim lId As Long, sDesc As String, dtDate As Date
'store the data in a variable when the forms opens
mvaData = Sheet1.Range("A1:C5")
'sort with larger dates on top
For i = LBound(mvaData, 1) To UBound(mvaData, 1) - 1
For j = i To UBound(mvaData, 1)
If mvaData(i, 3) < mvaData(j, 3) Then
lId = mvaData(j, 1)
sDesc = mvaData(j, 2)
dtDate = mvaData(j, 3)
mvaData(j, 1) = mvaData(i, 1)
mvaData(j, 2) = mvaData(i, 2)
mvaData(j, 3) = mvaData(i, 3)
mvaData(i, 1) = lId
mvaData(i, 2) = sDesc
mvaData(i, 3) = dtDate
End If
Next j
Next i
End Sub

Related

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.

My code will not work with my userform, why?

I am trying to set up a user form to do a loop and look up information in my table which is in a separate worksheet within the same workbook.
I want my user form to look up information in my table as I type and then auto fill in the other textboxes so that I can limit keystrokes and duplicates.
I found some code that worked with another user form as desired. However, when I try to use the same code for my table it goes through the loop like it’s looking but it does not populate the user form. I have tried changing with the user forms textbox names and making sure the names match... but to no avail. I also have to skip over a combo box on my user form, can this effect my code?
Option Explicit
Dim id As String, i As String, j As Integer, flag As Boolean
Sub GetData()
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 = 4 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 = 2 To 4
' UserForm1.Controls("TextBox" & j).Value = ""
Next j
End If
Else
End If
End Sub
try qualifying your ranges up to worksheet reference:
Option Explicit
Sub GetDataA()
Dim id As String, i As String, j As Integer, flag As Boolean
If Not IsNumeric(UserForm1.TextBox1.Value) Then
flag = False
i = 0
id = UserForm1.TextBox1.Value
With Worksheets("myTableWorksheetName") '<--| change "myTableWorksheetName" to your actual worksheet with table name
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
End With
If flag = False Then
For j = 5 To 10
UserForm1.Controls("TextBox" & j).Value = ""
Next j
End If
End If
End Sub

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

Add codes for dynamically created Active x-check boxes using vba

Dim t As Long
Dim u As Long
Dim v As Long
Dim q As Long
Dim p As Long
t = 1
u = 1
Do
If Sheet2.Range("D" & t).Value = "" Then
If Sheet2.Range("D" & t + 1).Value = "" Then
If Sheet2.Range("D" & t + 2).Value = "" Then
If Sheet2.Range("D" & t + 3).Value = "" Then
If Sheet2.Range("D" & t + 4).Value = "" Then
If Sheet2.Range("C" & t).Value = "" Then
Exit Do
End If
End If
End If
End If
End If
End If
If Not Sheet2.Range("D" & t).Value = "" Then
If Not Sheet2.Range("D" & t).Value = "Description" Then
v = Sheet2.Range("A" & 1 & ":" & "A" & t - 1).Height
q = Sheet2.Range("A" & t).Height
p = v + (q / 2) - 5
Set obj = Sheet2.OLEObjects.Add("Forms.checkbox.1")
With obj
.Width = 10
.Top = p
.Left = 875
.Height = 10
End With
u = u + 1
End If
End If
t = t + 1
Loop
This Code will help me to create many active-x check boxes as per my requirement as u can see in the image.
check the image,after i click the necessary check boxes,and then the command button "export the nfr", the row corresponding to the selected check box should be copied to another sheet, is there any way to add codes for that manipulation
sorry for editing the question
https://i.stack.imgur.com/YF2U2.png
Use a "custom" check box, by creating an event sunk class, such as this, clsCustomCheckBox
Option Explicit
Public WithEvents cb As msforms.CheckBox
Public Sub init(cbInit As msforms.CheckBox)
Set cb = cbInit
End Sub
Private Sub cb_Click() ' or the _Change event....
' Your code here
End Sub
You could then add your new ones, afterwards doing something similar to the below
Private c As Collection
Sub testcb()
Dim o As Object
Dim cb As New clsCustomCheckBox
Set o = ActiveSheet.OLEObjects(1)
cb.init o.Object
Set c = New Collection
c.Add cb
End Sub
you could switch to a Form Control instead of an ActiveX one and take advantage of its OnAction property and assign the same sub to all checkboxes
as follows:
Sub Macro2()
Dim t As Long, u As Long, v As Long, q As Long, p As Long
t = 2 '<--| start from 2 otherwise subsequent "A" & (t - 1) would return "A0"!
u = 1
With Sheet2
Do
If WorksheetFunction.CountA(.Cells(t, "D").Resize(5), .Cells(t, "C")) < 6 Then Exit Do
If Not .Cells(t, "D").Value = "Description" Then
v = .Range("A1", "A" & (t - 1)).Height
q = .Cells(t, "A").Height
p = v + (q / 2) - 5
With .CheckBoxes.Add(875, p, 10, 10) '<--| add a 'Form' checkbox
.OnAction = "CheckBoxClick" '<--| current checkbox will "react" calling 'CheckBoxClick()' sub
End With
u = u + 1 '<--| what is this for?
End If
t = t + 1
Loop
End With
End Sub
then you only have to type your CheckBoxClick() sub, for instance:
Sub CheckBoxClick()
With ActiveSheet.CheckBoxes(Application.Caller) '<--| reference caller checkbox
MsgBox "hello from " & .Name & " place at cell " & .TopLeftCell.Address
End With
End Sub

Combo box not returning chosen listindex value

I'm creating a workbook, which tracks available rentals per month. It is divided into 12 sheets, one for each month. The first three columns of each sheet track the type of accommodation, number of bedrooms and what's included in the rental price. The concept there is that there will be a drop-down combo box that allows the user to fill in with a point-and-click option rather than typing things out in order to reduce input errors.
I set up a fixed array, the contents in which changes depending on what column that active cell is in, and then the array is assigned to the combo box. The code lives in the Sheet1 Module under the combo box code and the ThisWorkbook module calls it under SheetSelectionChange, so as to avoid repeating the code in each sheet.
A Standard Module makes the array public
All 12 combo boxes share the same name, cboOptions, and they populate correctly, regardless of what sheet is chosen. My problem is that none of the combo boxes return the listindex value of the choice that's made, regardless of the code telling it to do so. I've been testing to see the value of the position returned against the value of the position chosen, but I have not been able to establish a pattern. I thought about clearing the variables and arrays, thinking that might be what's messing with the code, but it seems to be having no effect. I've read what I could on the issue, but I'm out of ideas on what might be the problem...thank you in advance!
Code in Sheet1 module:
Private Sub cboOptions_Change()
Erase myarray()
cboOptions.Visible = True
cboOptions.Enabled = True
cboOptions.Clear
n = ActiveCell.Row
If n >= 3 And n < 10000 Then
If ActiveSheet.Range(ActiveCell.Address).Address = Range("A" & n).Address Then
myarray(1) = "Apartment"
myarray(2) = "Room"
myarray(3) = "Townhouse"
myarray(4) = "House"
ElseIf ActiveSheet.Range(ActiveCell.Address).Address = Range("B" & n).Address Then
myarray(1) = "1"
myarray(2) = "2"
myarray(3) = "3"
myarray(4) = "4"
myarray(5) = "5"
ElseIf ActiveSheet.Range(ActiveCell.Address).Address = Range("C" & n).Address Then
myarray(1) = "Heat & Water"
myarray(2) = "All-inclusive"
Else
cboOptions.Enabled = False
cboOptions.Visible = False
End If
End If
'ActiveSheet.cboOptions.ListIndex = 0
'Dim x As Long
'MsgBox ActiveSheet.Name
With ActiveSheet
.cboOptions.Left = .Range(ActiveCell.Address).Left
.cboOptions.Top = .Range(ActiveCell.Address).Top
.cboOptions.List = myarray()
With .cboOptions
'the problem is that x needs to get assigned a value from the combo box before it continues to execute
x = .List(.ListIndex)
'MsgBox x
End With
.Range(ActiveCell.Address) = x 'myarray(x)
.Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
x = 0
Erase myarray()
End With
End Sub
Code in ThisWorkbook:
Sub Workbook_SheetSelectionChange(ByVal sh As Object, ByVal Target As Range)
Application.Run "Sheet1.cboOptions_Change"
End Sub
Code in Module1:
Option Explicit
Public myarray(0 To 5) As String
The nature of the problem seems to be that using more than one array for one combo box breaks down how the listindex values are calculated. I broke down the code to its component features to see if the issue persisted
1) Made a new file and put the code in Sheet1
2) Made separate fixed arrays for each kind of input
3) Created a separate routine for each kind of input
Using ON ERROR RESUME NEXT at the beginning of each routine overlooks the error and the code works properly. Alternatively, putting in a break where the integer variable is given the listindex value of the combo box allows the user to make a choice and assign a value to the integer variable, before continuing. Otherwise, its default value is -1 and returns an error; using .list(.listindex) did not make any difference, suggesting that the code needs to wait for user input (using a combobox event other than Change?).
May just need to establish a separate combo box for each column. Anyway, the code below is the sticks-and-stones version of the above, for a single sheet, and it will do the job if applied to each sheet module in the workbook:
Sub monthnames()
'add month names to the first cell of each sheet
Dim n As Integer
'Sheets(1).Activate
For n = 1 To 12
Sheets.Add After:=ActiveSheet
ThisWorkbook.Sheets(n).Cells(1, 1) = MonthName(n)
Next
End Sub
Private Sub cboOptions_Change()
Dim myarray(1 To 4) As String
Dim myarray2(1 To 5) As String
Dim myarray3(1 To 2) As String
cboOptions.Enabled = True
cboOptions.Visible = True
Dim n As Integer
n = ActiveCell.Row
If n >= 3 And n < 10000 Then
If Range(ActiveCell.Address).Address = Range("A" & n).Address Then
myarray(1) = "Apartment"
myarray(2) = "Room"
myarray(3) = "Townhouse"
myarray(4) = "House"
cboOptions.List = myarray()
inputdata myarray(), n
ElseIf Range(ActiveCell.Address).Address = Range("B" & n).Address Then
myarray2(1) = "1"
myarray2(2) = "2"
myarray2(3) = "3"
myarray2(4) = "4"
myarray2(5) = "5"
cboOptions.List = myarray2()
inputdata2 myarray2(), n
ElseIf Range(ActiveCell.Address).Address = Range("C" & n).Address Then
myarray3(1) = "Heat & Water"
myarray3(2) = "All-inclusive"
cboOptions.List = myarray3()
inputdata3 myarray3(), n
Else
cboOptions.Enabled = False
cboOptions.Visible = False
End If
End If
End Sub
Sub inputdata(myarray, n) 'myarray3, )
On Error Resume Next
Dim x As Integer
cboOptions.Left = Range(ActiveCell.Address).Left
cboOptions.Top = Range(ActiveCell.Address).Top
Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
If Range(ActiveCell.Address).Address = Range("A" & n).Address Then
x = cboOptions.ListIndex + 1
Range(ActiveCell.Address) = myarray(x)
Else
Exit Sub
End If
End Sub
Sub inputdata2(myarray2, n)
On Error Resume Next
Dim y As Integer
cboOptions.Left = Range(ActiveCell.Address).Left
cboOptions.Top = Range(ActiveCell.Address).Top
Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
If Range(ActiveCell.Address).Address = Range("B" & n).Address Then
y = cboOptions.ListIndex + 1
Range(ActiveCell.Address) = myarray2(y)
Else
Exit Sub
End If
End Sub
Sub inputdata3(myarray3, n)
On Error Resume Next
Dim z As Integer
cboOptions.Left = Range(ActiveCell.Address).Left
cboOptions.Top = Range(ActiveCell.Address).Top
Columns(ActiveCell.Column).ColumnWidth = cboOptions.Width * 0.18
If Range(ActiveCell.Address).Address = Range("C" & n).Address Then
z = cboOptions.ListIndex + 1
Range(ActiveCell.Address) = myarray3(z)
Else
Exit Sub
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Call cboOptions_Change
End Sub