
📎 This article includes 2 downloadable practice files ↓
Shared sheets get messy because people type straight into them: dates in three formats, “Delhi ” with a trailing space, an amount typed as text, a row added below the totals. A UserForm puts a proper window in front of the sheet. People pick from lists, the form checks what they entered, and only clean data reaches your table.
In this article
We’ll build a small expense-entry form: date, category (from a list), amount, paid by, note.
1. Create the form
- In the VBA editor: Insert › UserForm.
- Press F4 for the Properties window and set (Name) to
frmExpenseand Caption to “Add expense”. - From the Toolbox (View › Toolbox if it’s hidden), drag on: 5 Labels, 3 TextBoxes, 2 ComboBoxes and 2 CommandButtons.
Name the controls — this matters more than anything else on a form, because the code refers to them by name:
| Control | (Name) | Purpose |
|---|---|---|
| TextBox | txtDate | Date |
| ComboBox | cboCategory | Category from a list |
| TextBox | txtAmount | Amount |
| ComboBox | cboPaidBy | Cash / Card / UPI |
| TextBox | txtNote | Optional note |
| CommandButton | cmdSave | Save (set Default = True so Enter saves) |
| CommandButton | cmdClose | Close (set Cancel = True so Esc closes) |
2. Fill the lists when the form opens
Double-click the form background to open its code, then choose UserForm › Initialize from the drop-downs:
Private Sub UserForm_Initialize()
txtDate.Value = Format(Date, "dd-mm-yyyy")
cboCategory.List = ThisWorkbook.Worksheets("Lists").Range("A2:A12").Value
cboPaidBy.List = Array("Cash", "Card", "UPI", "Bank transfer")
cboCategory.Style = fmStyleDropDownList ' pick only, no typing
cboPaidBy.Style = fmStyleDropDownList
End Sub
Reading the categories from a Lists sheet means anyone can add a category without touching the code.
3. Validate, then save
Private Sub cmdSave_Click()
' --- check every input before writing anything ---
If Not IsDate(txtDate.Value) Then
MsgBox "Please enter a valid date (dd-mm-yyyy).", vbExclamation: txtDate.SetFocus: Exit Sub
End If
If cboCategory.ListIndex = -1 Then
MsgBox "Pick a category.", vbExclamation: cboCategory.SetFocus: Exit Sub
End If
If Not IsNumeric(txtAmount.Value) Or Val(txtAmount.Value) <= 0 Then
MsgBox "Amount must be a number above zero.", vbExclamation: txtAmount.SetFocus: Exit Sub
End If
If cboPaidBy.ListIndex = -1 Then
MsgBox "How was it paid?", vbExclamation: cboPaidBy.SetFocus: Exit Sub
End If
' --- add a row to the table ---
Dim tbl As ListObject, r As ListRow
Set tbl = ThisWorkbook.Worksheets("Expenses").ListObjects("tblExpenses")
Set r = tbl.ListRows.Add
r.Range.Value = Array(CDate(txtDate.Value), cboCategory.Value, CDbl(txtAmount.Value), _
cboPaidBy.Value, Trim$(txtNote.Value), Environ$("USERNAME"))
' --- ready for the next entry ---
txtAmount.Value = "": txtNote.Value = "": cboCategory.ListIndex = -1
cboCategory.SetFocus
End Sub
Private Sub cmdClose_Click()
Unload Me
End Sub
Writing into an Excel Table (Ctrl+T) instead of “the next empty row” is the key decision: the table grows by itself, formulas and formatting copy down, and pivot tables based on it pick up the new rows.
CDate("05-06-2026") follows the PC’s regional settings. On a PC set to US format it becomes 6 May instead of 5 June. For shared files, use a date picker or three boxes (day, month, year) and DateSerial.4. Show the form from a button
In a normal module:
Sub ShowExpenseForm()
frmExpense.Show
End Sub
Then Insert › Shapes on the sheet, right-click the shape, Assign Macro, pick ShowExpenseForm.
Modal or modeless?
frmExpense.Show is modal: users can’t touch the sheet until the form closes. frmExpense.Show vbModeless lets them scroll the sheet while the form stays open — nice for look-ups, but then your code must cope with the sheet changing underneath it.
Where people go wrong
| Problem | Fix |
|---|---|
| “Object required” on a control name | The control’s (Name) doesn’t match the code — check spelling in Properties |
| Amount saved as text | Convert with CDbl() before writing |
| Form remembers old values | Unload Me instead of Me.Hide when closing |
| List shows only the first column | Set ColumnCount and ColumnWidths for multi-column lists |
Practice
The workbook has the finished form, a Lists sheet, the tblExpenses table with a monthly summary pivot, and a second, unfinished form for you to complete (a customer form with mobile-number validation). For a bigger example see the full data-entry project.
Next: Collections and Dictionaries — the fastest way to find unique values, count and group in VBA.
📎 Practice files for this article
- 📗Data-entry starter workbookTable, lists and step-by-step set-up for the form in this lesson.⬇ XLSX · 7 KB
- 📄userform-code.txtComplete form code with validation, ready to paste.⬇ TXT · 1 KB
Free to use for learning. Files with macros (.bas) are plain text — import them with Alt+F11 → File → Import File, and always test on a copy.
Stuck on a step? Ask a question and the AI answers using this article.