Create, edit and delete persistent records
Complete a CRUD workflow with selection, refresh and export.
Treat the database write and the refreshed screen as two steps. Read the selected record identity before updating or deleting; never use a visual row position as a primary key. Parameterized commands keep entered values separate from SQL structure. Requery after a successful write so the screen reflects committed data.
Step 1Prepare a safe practice project#
Extract the archive into a new writable folder, separate from your own application. Open CustomerManager.mcproj in McBasic™. Keep every declared source, layout, resource and migration file together.
To reconstruct it yourself, create a DesktopApp project and add the files declared below. In the complete project, follow these steps as a guided rebuild: replace each named source file once and Check after all its procedures are present.
Exact project configuration
{
"schemaVersion": 1,
"projectId": "3e098138-bd6b-4d47-b1b0-77fa484c0b82",
"name": "CustomerManager",
"kind": "DesktopApp",
"language": {
"dialect": "McBasic1",
"requireExplicit": true,
"compare": "Binary",
"defaultArrayBase": 0
},
"application": {
"bundleIdentifier": "com.mcbasic.samples.customermanager",
"version": "0.1.0",
"buildNumber": "1",
"startupFormId": "form-main",
"shutdownMode": "OnStartupFormClosed"
},
"forms": [
{
"id": "form-main",
"typeName": "MainForm",
"layout": "Forms/MainForm.form.json",
"code": "Forms/MainForm.bas"
}
],
"modules": [],
"classes": [],
"tests": [],
"references": [],
"resources": [],
"targets": [
"osx-arm64"
],
"capabilities": []
}Step 2Build the Form and set its properties#
MainForm
Select the Form first and set its properties. Add containers before their children. These are initial Designer values, not statements to paste into a procedure.
| Form property | Value |
|---|---|
Caption | "CustomerManager — McBasic" |
Width | 900 |
Height | 620 |
MinWidth | 760 |
MinHeight | 520 |
| Type | Name | Parent |
|---|---|---|
| Label | lblName | Form |
| TextBox | txtName | Form |
| Label | lblEmail | Form |
| TextBox | txtEmail | Form |
| CommandButton | cmdSave | Form |
| CommandButton | cmdDelete | Form |
| CommandButton | cmdRefresh | Form |
| CommandButton | cmdExport | Form |
| Label | lblStatus | Form |
| CommandButton | cmdSeed | Form |
| CommandButton | cmdClear | Form |
| CommandButton | cmdQuit | Form |
| DataGrid | grdCustomers | Form |
lblName · exact Inspector values
| Property | Value |
|---|---|
Left | 24 |
Top | 24 |
Width | 90 |
Height | 30 |
Caption | "Name" |
TabIndex | 0 |
| Anchor edges | Left, Top |
txtName · exact Inspector values
| Property | Value |
|---|---|
Left | 120 |
Top | 20 |
Width | 250 |
Height | 36 |
Text | "" |
TabIndex | 1 |
| Anchor edges | Left, Top |
lblEmail · exact Inspector values
| Property | Value |
|---|---|
Left | 390 |
Top | 24 |
Width | 80 |
Height | 30 |
Caption | "Email" |
TabIndex | 2 |
| Anchor edges | Left, Top |
txtEmail · exact Inspector values
| Property | Value |
|---|---|
Left | 470 |
Top | 20 |
Width | 250 |
Height | 36 |
Text | "" |
TabIndex | 3 |
| Anchor edges | Left, Top |
cmdSave · exact Inspector values
| Property | Value |
|---|---|
Left | 24 |
Top | 72 |
Width | 140 |
Height | 36 |
Caption | "Save" |
TabIndex | 4 |
| Anchor edges | Left, Top |
cmdDelete · exact Inspector values
| Property | Value |
|---|---|
Left | 176 |
Top | 72 |
Width | 140 |
Height | 36 |
Caption | "Delete" |
TabIndex | 5 |
| Anchor edges | Left, Top |
cmdRefresh · exact Inspector values
| Property | Value |
|---|---|
Left | 328 |
Top | 72 |
Width | 140 |
Height | 36 |
Caption | "Refresh" |
TabIndex | 6 |
| Anchor edges | Left, Top |
cmdExport · exact Inspector values
| Property | Value |
|---|---|
Left | 480 |
Top | 72 |
Width | 140 |
Height | 36 |
Caption | "Export CSV" |
TabIndex | 7 |
| Anchor edges | Left, Top |
lblStatus · exact Inspector values
| Property | Value |
|---|---|
Left | 490 |
Top | 116 |
Width | 380 |
Height | 32 |
Caption | "Starting..." |
TabIndex | 11 |
| Anchor edges | Left, Top |
cmdSeed · exact Inspector values
| Property | Value |
|---|---|
Left | 24 |
Top | 112 |
Width | 150 |
Height | 36 |
Caption | "Load 10,000" |
TabIndex | 8 |
| Anchor edges | Left, Top |
cmdClear · exact Inspector values
| Property | Value |
|---|---|
Left | 186 |
Top | 112 |
Width | 120 |
Height | 36 |
Caption | "Clear" |
TabIndex | 9 |
| Anchor edges | Left, Top |
cmdQuit · exact Inspector values
| Property | Value |
|---|---|
Left | 318 |
Top | 112 |
Width | 100 |
Height | 36 |
Caption | "Quit" |
TabIndex | 10 |
| Anchor edges | Left, Top |
grdCustomers · exact Inspector values
| Property | Value |
|---|---|
Left | 24 |
Top | 164 |
Width | 846 |
Height | 398 |
TabIndex | 12 |
IsReadOnly | True |
CanUserSortColumns | True |
CanUserResizeColumns | True |
HeadersVisible | True |
| Anchor edges | Left, Top |
Step 3Connect the events explicitly#
MainForm
| Object | Event | Procedure | Parameters |
|---|---|---|---|
cmdSave | Click | cmdSave_Click | Click() |
cmdDelete | Click | cmdDelete_Click | Click() |
cmdRefresh | Click | cmdRefresh_Click | Click() |
cmdExport | Click | cmdExport_Click | Click() |
cmdSeed | Click | cmdSeed_Click | Click() |
cmdClear | Click | cmdClear_Click | Click() |
cmdQuit | Click | cmdQuit_Click | Click() |
grdCustomers | SelectionChanged | grdCustomers_SelectionChanged | SelectionChanged() |
| Form | Shown | Form_Shown | () |
Step 4Enter the complete behavior#
Open the named source file. Enter declarations first, then every procedure. Expand a procedure for its full body, or copy the complete file. Helpers are required: do not stop after entering the first Click handler.
Forms/MainForm.bas
Enter these declarations at file scope, before the procedures.
Option Explicit
Private mDb As Database
Private mRows As BindingSource
Private mSelectedId As Long
Private mSelectedRow As Long
Private mDeleteArmed As Boolean
Private mBusy As BooleanPrivate Async Sub Form_Shown()
Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.
Private Async Sub Form_Shown()
Dim migrations As MigrationSet
Dim version As Long
Dim ignored As Variant
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
cmdExport.Enabled = False
lblStatus.Caption = "Opening SQLite database..."
Try
Set mDb = Await Sqlite.OpenAsync(AppPaths.DataFile("customers.db"))
Set migrations = Sqlite.CreateMigrationSet()
migrations.Add 1, "CREATE TABLE IF NOT EXISTS Customers (ID INTEGER PRIMARY KEY, Name TEXT NOT NULL, Email TEXT NULL, CreatedAt TEXT NOT NULL)"
version = Await mDb.ApplyMigrationsAsync(migrations)
ignored = Await RefreshCustomers()
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdExport.Enabled = True
mBusy = False
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) ready"
Catch failure As McException
mBusy = False
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End SubPrivate Async Sub cmdExport_Click()
Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.
Private Async Sub cmdExport_Click()
Dim command As DbCommand
Dim rows As Recordset
If mBusy Then Exit Sub
If mDb Is Nothing Then Exit Sub
mBusy = True
cmdExport.Enabled = False
lblStatus.Caption = "Exporting customers.csv..."
Try
Set command = mDb.CreateCommand("SELECT ID, Name, Email, CreatedAt FROM Customers ORDER BY ID")
Set rows = Await command.QueryAsync()
Await rows.ExportCsvAsync(AppPaths.DataFile("customers.csv"))
lblStatus.Caption = CStr(rows.RowCount) & " customer(s) exported to customers.csv"
Catch failure As McException
If failure.Number = 58 Then
lblStatus.Caption = "customers.csv already exists; rename or remove it before exporting again."
Else
lblStatus.Caption = "CSV error " & CStr(failure.Number) & ": " & failure.Message
End If
Finally
mBusy = False
cmdExport.Enabled = True
End Try
End SubPrivate Async Function RefreshCustomers() As Boolean
Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.
Private Async Function RefreshCustomers() As Boolean
Dim command As DbCommand
Dim rows As Recordset
Try
Set command = mDb.CreateCommand("SELECT ID, Name, Email, CreatedAt FROM Customers ORDER BY ID")
Set rows = Await command.QueryAsync()
Set mRows = rows.Bind()
mRows.Require "Name"
Set grdCustomers.DataSource = mRows
grdCustomers.SelectedIndex = -1
mSelectedId = 0
mSelectedRow = -1
mDeleteArmed = False
txtName.Text = ""
txtEmail.Text = ""
RefreshCustomers = True
Catch failure As McException
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
RefreshCustomers = False
End Try
End FunctionPrivate Async Sub cmdSave_Click()
Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.
Private Async Sub cmdSave_Click()
Dim command As DbCommand
Dim email As Variant
Dim wasUpdate As Boolean
Dim ignored As Variant
If mBusy Then Exit Sub
If Len(Trim$(txtName.Text)) = 0 Then
lblStatus.Caption = "Name is required."
txtName.SetFocus
Exit Sub
End If
If Len(txtEmail.Text) = 0 Then
email = Null
Else
email = txtEmail.Text
End If
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
Try
wasUpdate = (mSelectedId <> 0)
If Not wasUpdate Then
Set command = mDb.CreateCommand("INSERT INTO Customers (Name, Email, CreatedAt) VALUES (@name, @email, datetime('now'))")
command.AddParameter "@name", txtName.Text
command.AddParameter "@email", email
End If
If wasUpdate Then
mRows.BeginEdit()
mRows.SetValue mSelectedRow, "Name", txtName.Text
mRows.SetValue mSelectedRow, "Email", email
If Not mRows.Validate() Then
lblStatus.Caption = mRows.ValidationError
mRows.CancelEdit()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
Exit Sub
End If
ignored = Await mRows.SaveAsync(mDb, "Customers", "ID")
Else
Await command.ExecuteAsync()
End If
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
If wasUpdate Then
lblStatus.Caption = "Customer updated."
Else
lblStatus.Caption = "Customer saved."
End If
Catch failure As McException
If wasUpdate Then
mRows.CancelEdit()
End If
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End SubPrivate Async Sub cmdDelete_Click()
Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.
Private Async Sub cmdDelete_Click()
Dim ignored As Variant
If mBusy Then Exit Sub
If mSelectedId = 0 Then
lblStatus.Caption = "Select a customer first."
Exit Sub
End If
If Not mDeleteArmed Then
mDeleteArmed = True
lblStatus.Caption = "Click Delete again to confirm removal."
Exit Sub
End If
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
Try
mRows.BeginEdit()
mRows.Delete mSelectedRow
ignored = Await mRows.SaveAsync(mDb, "Customers", "ID")
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Customer deleted."
Catch failure As McException
mRows.CancelEdit()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End SubPrivate Async Sub cmdRefresh_Click()
Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.
Private Async Sub cmdRefresh_Click()
Dim ignored As Variant
If mBusy Then Exit Sub
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
Try
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) loaded"
Catch failure As McException
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End SubPrivate Async Sub cmdSeed_Click()
Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.
Private Async Sub cmdSeed_Click()
Dim command As DbCommand
Dim ignored As Variant
If mBusy Then Exit Sub
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
cmdSeed.Enabled = False
cmdClear.Enabled = False
cmdQuit.Enabled = False
lblStatus.Caption = "Loading 10,000 customers..."
Try
Set command = mDb.CreateCommand("WITH digits(n) AS (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9), seed(n) AS (SELECT d1.n + d2.n * 10 + d3.n * 100 + d4.n * 1000 + 1 FROM digits d1 CROSS JOIN digits d2 CROSS JOIN digits d3 CROSS JOIN digits d4) INSERT INTO Customers (Name, Email, CreatedAt) SELECT 'Customer ' || n, 'customer' || n || '@example.com', datetime('now') FROM seed")
Await command.ExecuteAsync()
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) loaded"
Catch failure As McException
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End SubPrivate Async Sub cmdClear_Click()
Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.
Private Async Sub cmdClear_Click()
Dim command As DbCommand
Dim ignored As Variant
If mBusy Then Exit Sub
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
cmdSeed.Enabled = False
cmdClear.Enabled = False
cmdQuit.Enabled = False
lblStatus.Caption = "Clearing customers..."
Try
Set command = mDb.CreateCommand("DELETE FROM Customers")
Await command.ExecuteAsync()
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) loaded"
Catch failure As McException
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End SubPrivate Sub cmdQuit_Click()
Enter the complete action, including its input checks and calls to helper procedures.
Private Sub cmdQuit_Click()
App.Quit
End SubPrivate Sub grdCustomers_SelectionChanged()
Read the current value or selection and update its dependent interface. The binding above determines which object calls this handler.
Private Sub grdCustomers_SelectionChanged()
Dim row As Long
If mRows Is Nothing Then Exit Sub
row = CInt(grdCustomers.SelectedIndex)
If row < 0 Or row >= mRows.RowCount Then Exit Sub
mSelectedId = CLng(mRows.Value(row, "ID"))
mSelectedRow = row
txtName.Text = CStr(mRows.Value(row, "Name"))
If IsNull(mRows.Value(row, "Email")) Then
txtEmail.Text = ""
Else
txtEmail.Text = CStr(mRows.Value(row, "Email"))
End If
mDeleteArmed = False
lblStatus.Caption = "Editing customer " & CStr(mSelectedId)
End SubCopy the complete Forms/MainForm.bas file
Replace the file once; do not append duplicate procedures.
Option Explicit
Private mDb As Database
Private mRows As BindingSource
Private mSelectedId As Long
Private mSelectedRow As Long
Private mDeleteArmed As Boolean
Private mBusy As Boolean
Private Async Sub Form_Shown()
Dim migrations As MigrationSet
Dim version As Long
Dim ignored As Variant
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
cmdExport.Enabled = False
lblStatus.Caption = "Opening SQLite database..."
Try
Set mDb = Await Sqlite.OpenAsync(AppPaths.DataFile("customers.db"))
Set migrations = Sqlite.CreateMigrationSet()
migrations.Add 1, "CREATE TABLE IF NOT EXISTS Customers (ID INTEGER PRIMARY KEY, Name TEXT NOT NULL, Email TEXT NULL, CreatedAt TEXT NOT NULL)"
version = Await mDb.ApplyMigrationsAsync(migrations)
ignored = Await RefreshCustomers()
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdExport.Enabled = True
mBusy = False
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) ready"
Catch failure As McException
mBusy = False
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End Sub
Private Async Sub cmdExport_Click()
Dim command As DbCommand
Dim rows As Recordset
If mBusy Then Exit Sub
If mDb Is Nothing Then Exit Sub
mBusy = True
cmdExport.Enabled = False
lblStatus.Caption = "Exporting customers.csv..."
Try
Set command = mDb.CreateCommand("SELECT ID, Name, Email, CreatedAt FROM Customers ORDER BY ID")
Set rows = Await command.QueryAsync()
Await rows.ExportCsvAsync(AppPaths.DataFile("customers.csv"))
lblStatus.Caption = CStr(rows.RowCount) & " customer(s) exported to customers.csv"
Catch failure As McException
If failure.Number = 58 Then
lblStatus.Caption = "customers.csv already exists; rename or remove it before exporting again."
Else
lblStatus.Caption = "CSV error " & CStr(failure.Number) & ": " & failure.Message
End If
Finally
mBusy = False
cmdExport.Enabled = True
End Try
End Sub
Private Async Function RefreshCustomers() As Boolean
Dim command As DbCommand
Dim rows As Recordset
Try
Set command = mDb.CreateCommand("SELECT ID, Name, Email, CreatedAt FROM Customers ORDER BY ID")
Set rows = Await command.QueryAsync()
Set mRows = rows.Bind()
mRows.Require "Name"
Set grdCustomers.DataSource = mRows
grdCustomers.SelectedIndex = -1
mSelectedId = 0
mSelectedRow = -1
mDeleteArmed = False
txtName.Text = ""
txtEmail.Text = ""
RefreshCustomers = True
Catch failure As McException
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
RefreshCustomers = False
End Try
End Function
Private Async Sub cmdSave_Click()
Dim command As DbCommand
Dim email As Variant
Dim wasUpdate As Boolean
Dim ignored As Variant
If mBusy Then Exit Sub
If Len(Trim$(txtName.Text)) = 0 Then
lblStatus.Caption = "Name is required."
txtName.SetFocus
Exit Sub
End If
If Len(txtEmail.Text) = 0 Then
email = Null
Else
email = txtEmail.Text
End If
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
Try
wasUpdate = (mSelectedId <> 0)
If Not wasUpdate Then
Set command = mDb.CreateCommand("INSERT INTO Customers (Name, Email, CreatedAt) VALUES (@name, @email, datetime('now'))")
command.AddParameter "@name", txtName.Text
command.AddParameter "@email", email
End If
If wasUpdate Then
mRows.BeginEdit()
mRows.SetValue mSelectedRow, "Name", txtName.Text
mRows.SetValue mSelectedRow, "Email", email
If Not mRows.Validate() Then
lblStatus.Caption = mRows.ValidationError
mRows.CancelEdit()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
Exit Sub
End If
ignored = Await mRows.SaveAsync(mDb, "Customers", "ID")
Else
Await command.ExecuteAsync()
End If
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
If wasUpdate Then
lblStatus.Caption = "Customer updated."
Else
lblStatus.Caption = "Customer saved."
End If
Catch failure As McException
If wasUpdate Then
mRows.CancelEdit()
End If
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End Sub
Private Async Sub cmdDelete_Click()
Dim ignored As Variant
If mBusy Then Exit Sub
If mSelectedId = 0 Then
lblStatus.Caption = "Select a customer first."
Exit Sub
End If
If Not mDeleteArmed Then
mDeleteArmed = True
lblStatus.Caption = "Click Delete again to confirm removal."
Exit Sub
End If
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
Try
mRows.BeginEdit()
mRows.Delete mSelectedRow
ignored = Await mRows.SaveAsync(mDb, "Customers", "ID")
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Customer deleted."
Catch failure As McException
mRows.CancelEdit()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End Sub
Private Async Sub cmdRefresh_Click()
Dim ignored As Variant
If mBusy Then Exit Sub
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
Try
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) loaded"
Catch failure As McException
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End Sub
Private Async Sub cmdSeed_Click()
Dim command As DbCommand
Dim ignored As Variant
If mBusy Then Exit Sub
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
cmdSeed.Enabled = False
cmdClear.Enabled = False
cmdQuit.Enabled = False
lblStatus.Caption = "Loading 10,000 customers..."
Try
Set command = mDb.CreateCommand("WITH digits(n) AS (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9), seed(n) AS (SELECT d1.n + d2.n * 10 + d3.n * 100 + d4.n * 1000 + 1 FROM digits d1 CROSS JOIN digits d2 CROSS JOIN digits d3 CROSS JOIN digits d4) INSERT INTO Customers (Name, Email, CreatedAt) SELECT 'Customer ' || n, 'customer' || n || '@example.com', datetime('now') FROM seed")
Await command.ExecuteAsync()
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) loaded"
Catch failure As McException
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End Sub
Private Async Sub cmdClear_Click()
Dim command As DbCommand
Dim ignored As Variant
If mBusy Then Exit Sub
mBusy = True
cmdSave.Enabled = False
cmdDelete.Enabled = False
cmdRefresh.Enabled = False
cmdSeed.Enabled = False
cmdClear.Enabled = False
cmdQuit.Enabled = False
lblStatus.Caption = "Clearing customers..."
Try
Set command = mDb.CreateCommand("DELETE FROM Customers")
Await command.ExecuteAsync()
ignored = Await RefreshCustomers()
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = CStr(mRows.RowCount) & " customer(s) loaded"
Catch failure As McException
mBusy = False
cmdSave.Enabled = True
cmdDelete.Enabled = True
cmdRefresh.Enabled = True
cmdSeed.Enabled = True
cmdClear.Enabled = True
cmdQuit.Enabled = True
lblStatus.Caption = "Database error " & CStr(failure.Number) & ": " & failure.Message
End Try
End Sub
Private Sub cmdQuit_Click()
App.Quit
End Sub
Private Sub grdCustomers_SelectionChanged()
Dim row As Long
If mRows Is Nothing Then Exit Sub
row = CInt(grdCustomers.SelectedIndex)
If row < 0 Or row >= mRows.RowCount Then Exit Sub
mSelectedId = CLng(mRows.Value(row, "ID"))
mSelectedRow = row
txtName.Text = CStr(mRows.Value(row, "Name"))
If IsNull(mRows.Value(row, "Email")) Then
txtEmail.Text = ""
Else
txtEmail.Text = CStr(mRows.Value(row, "Email"))
End If
mDeleteArmed = False
lblStatus.Caption = "Editing customer " & CStr(mSelectedId)
End SubStep 5Keep resources and storage reproducible#
No external resource or migration files are declared. Any inline migration, seed data or local JSON is present in the code above. Runtime application data and private preferences are not included in the archive.
Step 6Check, run and test the result#
Save all files and run Check. Resolve diagnostics before Run. Developer CLI users can work from the extracted folder:
mcbasic check CustomerManager.mcproj
mcbasic run CustomerManager.mcprojAcceptance checklist
- Seed the sample records.
- Create a new customer and reopen the application to verify persistence.
- Select, edit and save that record.
- Delete the selected record and verify it is absent after refresh.
- Export and inspect the output.
Step 7Make one change and repeat the tests#
Add an empty-name guard before the save command creates any SQL command.
Save a working Git checkpoint first. Change one behavior, Check again and repeat the acceptance checklist. Compare with the downloaded solution if a regression appears.
20f4bfed6b · Build manifest