adding data from userform into table - excel

I created a userform to add data in excel.
The data is been added quite good but the problem is that they are added first in total row then outside the table I have created
here is the code I am using:
Sub Submit_Data()
Dim iRow As Long
If adminpanel.txtRowNumber.Value = "" Then
iRow = student.Range("A" & Rows.Count).End(xlUp).Row + 1
Else
iRow = adminpanel.txtRowNumber.Value
End If
With student.Range("A" & iRow)
.Offset(0, 0).Value = "=Row()-1"
.Offset(0, 1).Value = adminpanel.Studentname.Value
.Offset(0, 2).Value = adminpanel.Class.Value
.Offset(0, 3).Value = adminpanel.School.Value
.Offset(0, 4).Value = adminpanel.Mobile.Value
.Offset(0, 5).Value = adminpanel.Email.Value
.Offset(0, 6).Value = adminpanel.txtImagePath.Value
End With
Call Reset_Form
Application.ScreenUpdating = True
MsgBox "data are done"
End Sub

If you're working with a Table/ListObject then it has a ListRows.Add method which you should use when you need to add a new row. From that row you can get its Range property.
Sub Submit_Data()
Dim iRow As Long, tblRow As Range, lo As ListObject
Set lo = student.ListObjects(1) 'get a reference to your table
If Len(adminpanel.txtRowNumber.Value) = 0 Then
Set tblRow = lo.ListRows.Add.Range '<< add a new row and get its range
Else
'get a reference to the existing row
iRow = CLng(adminpanel.txtRowNumber.Value)
Set tblRow = Application.Intersect(student.Rows(iRow), _
lo.DataBodyRange)
End If
'Fill the row in one operation using an array
tblRow.Value = Array(tblRow.Row - 1, adminpanel.Studentname.Value, _
adminpanel.Class.Value, adminpanel.School.Value, _
adminpanel.Mobile.Value, adminpanel.Email.Value, _
adminpanel.txtImagePath.Value)
Reset_Form
MsgBox "data are done"
End Sub

Related

Object doesn't support this property or method when I try to run countif based on activex textbox

I am relatively new to this coding. I am trying to add to my inventory database(in another sheet) if the model that is key into the activex textbox dose not match. If it matches, then it will automatically update to the quantity. However, I am getting error438. Here is the code that I have written so far.
Sub Add()
Dim invdata As Worksheet
Dim frm As Worksheet
Dim iqty As Integer
Set frm = ThisWorkbook.Sheets("UserForm")
Set invdata = ThisWorkbook.Sheets("Inventory Database")
iqty = frm.Range("B9")
Dim irow As Integer
Dim jrow As Integer
Dim i As Integer
If Application.WorksheetFunction.CountIf(invdata.Range("C:C"), ActiveSheet.tbModel.Value) > 0 Then
jrow = invdata.Range("A" & invdata.Rows.Count).End(xlUp).row + 1
With invdata
.Cells(jrow, 1).Value = frm.Range("B6").Value
.Cells(jrow, 2).Value = frm.Range("B7").Value
.Cells(jrow, 3).Value = ActiveSheet.tbModel.Value
.Cells(jrow, 4).Value = frm.Range("B9").Value
End With
MsgBox ("New Model Added!")
Else
irow = invdata.Cells(Rows.Count, 3).End(xlUp).row
For i = 2 To irow
If Sheet1.Cells(i, 3) = ActiveSheet.tbModel.Value Then
Sheet1.Cells(i, 4) = Sheet1.Cells(i, 4) + iqty
Exit Sub
End If
Next i
End If
End Sub
Try this - using Find() instead of CountIf() saves you from the loop:
Sub Add()
Dim invdata As Worksheet, frm As Worksheet, model, f As Range
Dim iqty As Long
Set frm = ThisWorkbook.Sheets("UserForm")
Set invdata = ThisWorkbook.Sheets("Inventory Database")
iqty = frm.Range("B9").Value
model = frm.OLEObjects("tbModel").Object.Value '####
'see if there's a match using `Find()`
Set f = invdata.Range("C:C").Find(what:=model, lookat:=xlWhole)
If f Is Nothing Then
'model was not found in Col C
With invdata.Range("A" & invdata.Rows.Count).End(xlUp).Offset(1)
.Value = frm.Range("B6").Value
.Offset(0, 1).Value = frm.Range("B7").Value
.Offset(0, 2).Value = model
.Offset(0, 3).Value = iqty
End With
MsgBox "New Model Added!"
Else
With f.EntireRow.Cells(4)
.Value = .Value + iqty ' update qty in row `m`
End With
End If
End Sub

Previous and Next Button function to VBA Data Entry form is not working

Previous Record and Next Record sub routine is not working. I marked with 1 and 2. These two navigation bars (1&2) works on the what is entered on WaypointId.
Say for example, if I say waypoint id=1235, then next record should appear in a data entry form. My vba code is first search the row number of waypoint id in observation sheet and then I decrease the row number by 1 for displaying previous record and increase the row number by 1 for next record. Depends on the functionality it shows data in the Data Entry Form.
My VBA code is not working for those two things. Attach workbook with name Problem-1.xlsm See Navigation Control Module.
Sub FindRecord(WyPt)
Dim Value As String
WyPtRow = 0
ReadRow = 2
Value = Cells(ReadRow, 2)
While Value <> ""
If WyPt = Value Then
WyPtRow = ReadRow
Exit Sub
End If
ReadRow = ReadRow + 1
Value = Cells(ReadRow, 2)
Wend
End Sub
Sub ViewPreviousRecord()
Set DEFrm = Sheets("DataEntryForm")
Set ObsData = Sheets("Observations")
Dim WyPt As String
WyPt = Trim(DEFrm.Cells(6, 2))
Call FindRecord(WyPt)
LastRow = WyPtRow - 1
With DEFrm
.Cells(6, 2).Value = ObsData.Cells(LastRow, 2).Value 'WaypointID
.Cells(6, 4).Value = ObsData.Cells(LastRow, 3).Value 'ObsType
.Cells(8, 2).Value = ObsData.Cells(LastRow, 4).Value 'Date
.Cells(8, 4).Value = ObsData.Cells(LastRow, 5).Value 'LoggedBy
End With
End Sub
Sub ViewNextRecord()
Set DEFrm = Sheets("DataEntryForm")
Set ObsData = Sheets("Observations")
Dim WyPt As String
WyPt = Trim(DEFrm.Cells(6, 2))
Call FindRecord(WyPt)
LastRow = WyPtRow + 1
With DEFrm
.Cells(6, 2).Value = ObsData.Cells(LastRow, 2).Value 'WaypointID
.Cells(6, 4).Value = ObsData.Cells(LastRow, 3).Value 'ObsType
.Cells(35, 10).Value = ObsData.Cells(LastRow, 115) 'Photo4Desc
End With
End Sub
This is the most important procedure in your project.
Sub DisplayRecord(ByVal Rs As Long)
' 235
Dim Arr As Variant ' Data from row Rs in database
Dim Target() As String ' Dashboard addresses matching Arr
Dim i As Long ' loop counter: Arr(Index)
' cell addresses are aligned with column numbers in database (-2)
Arr = "B6,D6,B8,D8,G6,H6,G7,H7,G8,H8,B11,C11,D11,E11,F11,G11,H11,I11"
Arr = Arr & ",B14,C14,D14,E14,F14,G14,B17,C17,D17,E17,F17,G17"
Arr = Arr & ",I14,J14,I15,J15,I16,J16,I17,J17,B20,C20,D20,E20,F20,G20"
Arr = Arr & ",B23,C23,D23,E23,F23"
Arr = Arr & ",I20,J20,K20,I21,J21,K21,I2,J22,K22,I23,J23,K23"
Arr = Arr & ",B26,C26,D26,E26,F26,G26,H26,I26,J26,K26"
Arr = Arr & ",B27,C27,D27,E27,F27,G27,H27,I27,J27,K27"
Arr = Arr & ",B28,C28,D28,E28,F28,G28,H28,I28,J28,K28"
Arr = Arr & ",B29,C29,D29,E29,F29,G29,H29,I29,J29,K29"
Arr = Arr & ",B32,H32,I32,J32,H33,I33,J33,H34,I34,J34,H35,I35,J35"
Target = Split(Arr, ",")
With Sheets("Observations")
Arr = .Range(.Cells(Rs, 1), .Cells(Rs, 115)).Value
End With
Application.ScreenUpdating = False ' speed up execution
For i = 2 To UBound(Arr, 2) ' skip first database column
Sheets("DataEntryForm").Range(Target(i - 2)).Value = Arr(1, i)
Next i
Application.ScreenUpdating = True
End Sub
It displays the data of the row Rs given to it as an argument. You already have a function that finds the row number needed by the above procedure. Below please find an improvement.
Function RecordRow(ByVal WyPt As String) As Long
' 235
' return the row number where WyP was found or 0
Dim Fnd As Range
With Worksheets("Observations")
Set Fnd = .Range(.Cells(2, "B"), .Cells(.Rows.Count, "B").End(xlUp))
Set Fnd = Fnd.Find(WyPt, , LookIn:=xlValues, lookat:=xlWhole)
If Not Fnd Is Nothing Then
RecordRow = Fnd.Row
End If
End With
End Function
The deal is simple: you give the Waypoint ID and receive the row number where it was found. If it isn't found the function returns 0, and that is how you avoid crashes.
With these two procedures in place you can easily call up the first and the last records.
Sub ViewFirstRecord()
' 235
DisplayRecord 2
End Sub
Sub ViewLastRecord()
' 235
With Worksheets("Observations")
DisplayRecord .Cells(.Rows.Count, "A").End(xlUp).Row
End With
End Sub
The next and previous records are just a matter of finding the row number and displaying its data.
Sub ViewNextRecord()
' 235
Dim Rs As Long ' data source row
Rs = RecordRow(Trim(Cells(6, 2).Value)) + 1
If Rs > 1 Then
With Worksheets("Observations")
If Rs <= .Cells(.Rows.Count, "A").End(xlUp).Row Then
DisplayRecord Rs
Else
MsgBox "No more records to show.", vbInformation, "Last record"
End If
End With
End If
End Sub
Sub ViewPreviousRecord()
' 235
Dim Rs As Long ' data source row
Rs = RecordRow(Trim(Cells(6, 2).Value)) - 1
If Rs > 1 Then
DisplayRecord Rs
Else
MsgBox "No more records to show.", vbInformation, "First record"
End If
End Sub
If that's the whole code, you may be finding a problem with scope. It seems ViewPreviousRecord() is not able to see WyPtRow.
You can try adding
dim WyPtRow
Before the Sub FindRecord(WyPt) definition.
Another implementation would be changing the Sub for a function, and returning the WyPtRow value.

Add 10 entries from userform to sheet

I am looking for a way to shorten my code to input data from a form of 10 entries.
This is my userform with one RMA number (applies to all 10 PN), one customer name, 10 part numbers, and 10 serial numbers that go with each part number.
This is how I want data transferred to the worksheet.
The part number textboxes are named TB#.
The serial number textboxes are named SNTB#.
This is the code I have for the first entry. I was thinking of adding code to say "TB"&"i" and "SNTB"&"i", but I don't know where to place that statement or how to start it.
Private Sub EnterButton_Click()
'this assigns receiving data to first columns of log Sheet
If TB1.Value = "" Then
Else
Worksheets("RM Tracker").Activate
Dim lastrow
lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
lastrow = lastrow + 1
Cells(lastrow, 1) = RMATB.Value
Cells(lastrow, 2) = CustCB.Value
Cells(lastrow, 3) = TB1.Value
Cells(lastrow, 4) = SNTB1.Value
Cells(lastrow, 5) = ReceiveTB.Value
ActiveCell.Offset(1, 0).Select
End If
ActiveWorkbook.Save
Call resetform
End Sub
Sub resetform()
RMATB.Value = ""
CustCB.Value = ""
TB1.Value = ""
SNTB1.Value = ""
ReceiveTB = ""
'sets focus on that first textbox again
RecForm.RMATB.SetFocus
End Sub
You can incorporate a for loop where "i" represents the row you are working with. When you are appending data you need to put that reference within the loop so the new row is recalculated.
Private Sub EnterButton_Click()
'this assigns receiving data to first columns of log Sheet
If TB1.Value = "" Then
Else
Worksheets("RM Tracker").Activate
dim i as long
For i = 1 To 10
Dim lastrow as long ' should put a data type with dim statements
lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
lastrow = lastrow + 1
Cells(lastrow, 1) = Userform1.Controls("RMATB" & i).Value ' change userform name to fit your need
Cells(lastrow, 2) = Userform1.Controls("CustCB" & i).Value
Cells(lastrow, 3) = Userform1.Controls("TB1" & i).Value
Cells(lastrow, 4) = Userform1.Controls("SNTB1" & i).Value
Cells(lastrow, 5) = Userform1.Controls("ReceiveTB" & i).Value
Next i
End If
ActiveWorkbook.Save
Call resetform
End Sub
Sub resetform()
RMATB.Value = ""
CustCB.Value = ""
TB1.Value = ""
SNTB1.Value = ""
ReceiveTB = ""
'sets focus on that first textbox again
RecForm.RMATB.SetFocus

Update list with userform based on multiple criteria in VBA

I have a list with Name, Phone number, City and Dinner.
When the user fills out the user form they type in the abovementioned inputs.
The code updates the list if they fill in the same Name so the list does not append another row. I tried to edit the code such that it take into account Phone number too, but nothing changes.
However, how can I make the list add a new row if the user adds in the same name but different number?
Private Sub OKButton_Click()
Dim emptyRow As Long
'Make Sheet1 active
Sheet1.Activate
'Determine emptyRow
emptyRow = WorksheetFunction.CountA(Range("A:A")) + 1
' try to retrieve the Name
Dim rngIdList As Range, rngId As Range
Dim phoneIdList As Range, phoneId As Range
Set rngIdList = ActiveSheet.Range([a2], [a2].End(xlDown))
Set phoneIdList = ActiveSheet.Range([b2], [b2].End(xlDown))
Set rngId = rngIdList.Find(Me.NameTextBox.Value, LookIn:=xlValues)
Set phoneId = phoneIdList.Find(Me.PhoneTextBox.Value, LookIn:=xlValues)
If rngId Is Nothing And phoneId Is Nothing Then
' if Name is not found, append new one to the end of the table
With rngIdList And phoneIdList
Set rngId = .Offset(.Rows.Count, 0).Resize(1, 1)
Set phoneId = .Offset(.Rows.Count, 0).Resize(1, 1)
End With
End If
' update excel record
rngId.Offset(0, 0).Value = Me.NameTextBox.Value
rngId.Offset(0, 1).Value = Me.PhoneTextBox.Value
rngId.Offset(0, 2).Value = Me.CityListBox.Value
rngId.Offset(0, 3).Value = Me.DinnerComboBox.Value
phoneId.Offset(0, 0).Value = Me.NameTextBox.Value
phoneId.Offset(0, 1).Value = Me.PhoneTextBox.Value
phoneId.Offset(0, 2).Value = Me.CityListBox.Value
phoneId.Offset(0, 3).Value = Me.DinnerComboBox.Value
Expected output:
Here you can see that Jake adds his name multiple times with different phone numbers, but it doesn't get overwritten (as intended). However, if he adds number 888 again with different Dinner, it will get overwritten with Italian. If he adds number 222, then another row will be added to the list.
Try this. It's untested so let me know how you get on. I've added various explanatory comments.
Private Sub OKButton_Click()
Dim emptyRow As Long, s As String, bFound As Boolean
Sheet1.Activate
emptyRow = Range("A" & Rows.Count).End(xlUp).Row
Dim rngIdList As Range, rngId As Range
Set rngIdList = Range("A2:A" & emptyRow)
Set rngId = rngIdList.Find(Me.NameTextBox.Value, LookIn:=xlValues)
If rngId Is Nothing Then 'NAME NOT FOUND SO ADD NEW RECORD
With Range("A" & emptyRow + 1)
.Value = Me.NameTextBox.Value
.Offset(0, 1).Value = Me.PhoneTextBox.Value
.Offset(0, 2).Value = Me.CityListBox.Value
.Offset(0, 3).Value = Me.DinnerComboBox.Value
End With
Else 'NAME FOUND
s = rngId.Address
Do
If rngId.Offset(, 1).Value = Me.PhoneTextBox.Value Then 'PHONE NUMBER FOUND FOR SAME NAME SO UPDATE RECORD
With rngId
.Offset(0, 2).Value = Me.CityListBox.Value
.Offset(0, 3).Value = Me.DinnerComboBox.Value
End With
bFound = True
Exit Do 'NO NEED TO KEEP LOOKING
End If
Set rngId = rngIdList.FindNext(rngId)
Loop While rngId.Address <> s 'KEEP LOOKING UNTIL BACK TO FIRST FOUND VALUE
If Not bFound Then 'IF NAME/PHONE COMBO HAS NOT BEEN FOUND
With Range("A" & emptyRow + 1)
.Value = Me.NameTextBox.Value
.Offset(0, 1).Value = Me.PhoneTextBox.Value
.Offset(0, 2).Value = Me.CityListBox.Value
.Offset(0, 3).Value = Me.DinnerComboBox.Value
End With
End If
End If
End Sub
So many variables to make it easier to understand to you. Something like this (not tested, i didn't build userform)
Dim enteredName As String
enteredName = Me.NameTextBox.Value
Dim headerRow As Long
headerRow = 1 ' row containing headers
Dim lastDataRow As Long
lastDataRow = sheet1.Cells(Rows.Count, 1).End(xlUp).Row
Dim namesRng As Range
Set namesRng = sheet1.Range("A" & headerRow + 1 & ":A" & lastDataRow)
Dim position As Variant
' dim as Variant because if value not found by Match
' instead of Long we'll receive error
position = Application.Match(enteredName, namesRng, False)
Dim dataRow As Long ' row we'll add or update
If IsError(position) Then
dataRow = lastDataRow + 1 ' case: adding new row
Else
dataRow = position + headerRow ' case updating existing row
End If
With sheet1.Range("A" & dataRow)
.Value = enteredName ' not necessary when updating row, but maybe easier to read
.Offset(0, 1).Value = Me.PhoneTextBox.Value
.Offset(0, 2).Value = Me.CityListBox.Value
.Offset(0, 3).Value = Me.DinnerComboBox.Value
End With

VBA Userform data change check

I am using a userform to update data in a worksheet, I have an update command button to copy the data from the 'data' worksheet to the 'archive' and replace in the 'data' worksheet (essentially the 'archive' is a log of all previous lines and the 'data' is the most recent information)
The information is changed in text boxes and combo boxes
What Im struggling with is for the 'update' cmdbutton to first check if any changes where made before copying the data, if not I want a msg box to read 'no change in data, please close form'
Here is the code for the userform so far:
Private Sub cmdUpdate_Click()
' To write edited info of userform2 to Sheets("Data")
Dim LastRow As Long
Dim ABnum As Double
Dim ABrng As Range
Dim WriteRow As Long
'error statement
On Error GoTo errHandler:
'hold in memory and stop screen flicker
Application.ScreenUpdating = False
' Make sure we're on the right sheet
With Sheets("Data")
' Get the last row used so can set up the search range
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
' Set the range to search for the AB number
Set ABrng = .Range("A1:A" & LastRow)
' Get the AB number from what is selected on userform2
ABnum = txtup1.Value
' Get the row of sheet for this AB number
WriteRow = Application.Match(ABnum, ABrng, 0)
' Make this AB number the active cell
With .Cells(WriteRow, 1)
' Write in all the editable options
Sheets("Archive").Range("A" & Rows.Count).End(xlUp)(2).Resize(, 14).Value = .Resize(, 14).Value
.Offset(0, 4) = cboup3.Value
.Offset(0, 5) = cboup4.Value
.Offset(0, 6) = cboup5.Value
.Offset(0, 7) = cboup6.Value
.Offset(0, 8) = Date
.Offset(0, 9) = txtrev.Value
.Offset(0, 12) = txtup9.Value
.Offset(0, 13) = txtup8.Value
End With
End With
' Filter the Data
FilterMe
' Close the form
Unload Me
MsgBox ("Enquiry E0" + Me.txtup1.Text + " has been updated")
errHandler:
'Protect all sheets if error occurs
'Protect_All
'show error information in a messagebox
If Err.Number <> 0 Then
MsgBox "Error " & Err.Number & " just occured."
End If
End Sub
The easiest way would be to write a function to compare the values.
Private Sub cmdUpdate_Click()
' To write edited info of userform2 to Sheets("Data")
Dim LastRow As Long
Dim ABnum As Double
Dim ABrng As Range
Dim WriteRow As Long
'error statement
On Error GoTo errHandler:
'hold in memory and stop screen flicker
Application.ScreenUpdating = False
' Make sure we're on the right sheet
With Sheets("Data")
' Get the last row used so can set up the search range
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
' Set the range to search for the AB number
Set ABrng = .Range("A1:A" & LastRow)
' Get the AB number from what is selected on userform2
ABnum = txtenqup.Value
' Get the row of sheet for this AB number
WriteRow = Application.Match(ABnum, ABrng, 0)
' Make this AB number the active cell
With .Cells(WriteRow, 1)
'Check for changes
If Not hasValuePairsChanges(.Offset(0, 4).Value, cboup3.Value, _
.Offset(0, 5).Value, cboup4.Value, _
.Offset(0, 6).Value, cboup5.Value, _
.Offset(0, 7).Value, cboup6.Value, _
CDate(.Offset(0, 8).Value), Date, _
CDbl(.Offset(0, 9).Value), CDbl(txtrev.Value), _
.Offset(0, 12).Value, txtnotes.Value, _
.Offset(0, 13).Value, txtdtime.Value) Then
MsgBox "No Change in Data", vbInformation, ""
Exit Sub
End If
' Write in all the editable options
Sheets("Archive").Range("A" & Rows.Count).End(xlUp)(2).Resize(, 14).Value = .Resize(, 14).Value
.Offset(0, 4) = cboup3.Value
.Offset(0, 5) = cboup4.Value
.Offset(0, 6) = cboup5.Value
.Offset(0, 7) = cboup6.Value
.Offset(0, 8) = Date
.Offset(0, 9) = txtrev.Value
.Offset(0, 12) = txtnotes.Value
.Offset(0, 13) = txtdtime.Value
End With
End With
' Filter the Data
FilterMe
' Close the form
Unload Me
MsgBox ("Enquiry E0" + Me.txtenqup.Text + " has been updated")
errHandler:
'Protect all sheets if error occurs
'Protect_All
'show error information in a messagebox
If Err.Number <> 0 Then
MsgBox "Error " & Err.Number & " just occured."
End If
End Sub
Function hasValuePairsChanges(ParamArray Args() As Variant) As Boolean
Dim n As Long
For n = 0 To UBound(Args) Step 2
If Not Args(n) = Args(n + 1) Then
hasValuePairsChanges = True
Exit Function
End If
Next
End Function

Resources