Author a reusable BASIC control
Compose child controls behind public properties and a custom event.
CustomerId and CustomerName hide the internal TextBoxes behind a public property contract. OpenRequested communicates intent without forcing the consumer to know the inner button’s name. Do not expose every child control merely because it exists; keep a small stable API.
Step 1Prepare a safe practice project#
Extract the archive into a new writable folder, separate from your own application. Open CustomerCard.mcproj in McBasic™. Keep every declared source, layout, resource and migration file together.
To reconstruct it yourself, create a UserControlLibrary 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": "8c8e4e3f-3e45-4b7e-9a57-7b1e82e2b233",
"name": "CustomerCard",
"kind": "UserControlLibrary",
"language": {
"dialect": "McBasic1",
"requireExplicit": true,
"compare": "Binary",
"defaultArrayBase": 0
},
"package": {
"packageId": "com.mcbasic.samples.customercard",
"version": "1.0.0",
"minimumMcBasicVersion": "0.1.0",
"vendor": "McBasic Samples",
"description": "Two composed BASIC user controls.",
"assemblyPath": "lib/net10.0/CustomerCard.dll",
"licenseSpdx": "MIT",
"licensePath": "licenses/LICENSE.txt"
},
"forms": [],
"userControls": [
{
"id": "customer-card",
"typeName": "CustomerCard",
"layout": "Controls/CustomerCard.form.json",
"code": "Controls/CustomerCard.bas"
},
{
"id": "customer-badge",
"typeName": "CustomerBadge",
"layout": "Controls/CustomerBadge.form.json",
"code": "Controls/CustomerBadge.bas"
}
],
"modules": [],
"classes": [],
"tests": [],
"references": [],
"resources": [],
"targets": [
"osx-arm64"
],
"capabilities": []
}Step 2Build the Form and set its properties#
CustomerCard
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 | "Customer Card" |
Width | 320 |
Height | 120 |
MinWidth | 160 |
MinHeight | 80 |
| Type | Name | Parent |
|---|---|---|
| Label | lblId | Form |
| TextBox | txtId | Form |
| Label | lblName | Form |
| TextBox | txtName | Form |
| CommandButton | cmdOpen | Form |
lblId · exact Inspector values
| Property | Value |
|---|---|
Left | 16 |
Top | 12 |
Width | 80 |
Height | 28 |
Caption | "Customer ID" |
txtId · exact Inspector values
| Property | Value |
|---|---|
Left | 104 |
Top | 8 |
Width | 92 |
Height | 32 |
Text | "" |
ReadOnly | False |
MultiLine | False |
WordWrap | False |
lblName · exact Inspector values
| Property | Value |
|---|---|
Left | 16 |
Top | 56 |
Width | 80 |
Height | 28 |
Caption | "Name" |
txtName · exact Inspector values
| Property | Value |
|---|---|
Left | 104 |
Top | 52 |
Width | 92 |
Height | 32 |
Text | "" |
ReadOnly | False |
MultiLine | False |
WordWrap | False |
cmdOpen · exact Inspector values
| Property | Value |
|---|---|
Left | 208 |
Top | 52 |
Width | 96 |
Height | 32 |
Caption | "Open" |
CustomerBadge
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 | "Customer Badge" |
Width | 240 |
Height | 96 |
MinWidth | 160 |
MinHeight | 72 |
| Type | Name | Parent |
|---|---|---|
| TextBox | txtBadge | Form |
| CommandButton | cmdTap | Form |
txtBadge · exact Inspector values
| Property | Value |
|---|---|
Left | 12 |
Top | 12 |
Width | 136 |
Height | 32 |
Text | "Badge" |
ReadOnly | False |
MultiLine | False |
WordWrap | False |
cmdTap · exact Inspector values
| Property | Value |
|---|---|
Left | 156 |
Top | 12 |
Width | 72 |
Height | 32 |
Caption | "Tap" |
Step 3Connect the events explicitly#
CustomerCard
| Object | Event | Procedure | Parameters |
|---|---|---|---|
txtId | Change | txtId_Change | Change() |
txtName | Change | txtName_Change | Change() |
cmdOpen | Click | cmdOpen_Click | Click() |
CustomerBadge
| Object | Event | Procedure | Parameters |
|---|---|---|---|
txtBadge | Change | txtBadge_Change | Change() |
cmdTap | Click | cmdTap_Click | Click() |
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.
Controls/CustomerCard.bas
Enter these declarations at file scope, before the procedures.
Option Explicit
Public Event OpenRequested()Public Property Get CustomerId() As String
Keep this public property accessor with its complementary accessor and the fields or controls it uses.
Public Property Get CustomerId() As String
CustomerId = txtId.Text
End PropertyPublic Property Let CustomerId(ByVal value As String)
Keep this public property accessor with its complementary accessor and the fields or controls it uses.
Public Property Let CustomerId(ByVal value As String)
txtId.Text = value
End PropertyPublic Property Get CustomerName() As String
Keep this public property accessor with its complementary accessor and the fields or controls it uses.
Public Property Get CustomerName() As String
CustomerName = txtName.Text
End PropertyPublic Property Let CustomerName(ByVal value As String)
Keep this public property accessor with its complementary accessor and the fields or controls it uses.
Public Property Let CustomerName(ByVal value As String)
txtName.Text = value
End PropertyPrivate Sub cmdOpen_Click()
Enter the complete action, including its input checks and calls to helper procedures.
Private Sub cmdOpen_Click()
RaiseEvent OpenRequested
End SubPrivate Sub txtId_Change()
Read the current value or selection and update its dependent interface. The binding above determines which object calls this handler.
Private Sub txtId_Change()
End SubPrivate Sub txtName_Change()
Read the current value or selection and update its dependent interface. The binding above determines which object calls this handler.
Private Sub txtName_Change()
End SubCopy the complete Controls/CustomerCard.bas file
Replace the file once; do not append duplicate procedures.
Option Explicit
Public Event OpenRequested()
Public Property Get CustomerId() As String
CustomerId = txtId.Text
End Property
Public Property Let CustomerId(ByVal value As String)
txtId.Text = value
End Property
Public Property Get CustomerName() As String
CustomerName = txtName.Text
End Property
Public Property Let CustomerName(ByVal value As String)
txtName.Text = value
End Property
Private Sub cmdOpen_Click()
RaiseEvent OpenRequested
End Sub
Private Sub txtId_Change()
End Sub
Private Sub txtName_Change()
End SubControls/CustomerBadge.bas
Enter these declarations at file scope, before the procedures.
Option Explicit
Public Event Tapped()Public Property Get BadgeText() As String
Keep this public property accessor with its complementary accessor and the fields or controls it uses.
Public Property Get BadgeText() As String
BadgeText = txtBadge.Text
End PropertyPublic Property Let BadgeText(ByVal value As String)
Keep this public property accessor with its complementary accessor and the fields or controls it uses.
Public Property Let BadgeText(ByVal value As String)
txtBadge.Text = value
End PropertyPrivate Sub cmdTap_Click()
Enter the complete action, including its input checks and calls to helper procedures.
Private Sub cmdTap_Click()
RaiseEvent Tapped
End SubPrivate Sub txtBadge_Change()
Read the current value or selection and update its dependent interface. The binding above determines which object calls this handler.
Private Sub txtBadge_Change()
End SubCopy the complete Controls/CustomerBadge.bas file
Replace the file once; do not append duplicate procedures.
Option Explicit
Public Event Tapped()
Public Property Get BadgeText() As String
BadgeText = txtBadge.Text
End Property
Public Property Let BadgeText(ByVal value As String)
txtBadge.Text = value
End Property
Private Sub cmdTap_Click()
RaiseEvent Tapped
End Sub
Private Sub txtBadge_Change()
End SubStep 5Keep resources and storage reproducible#
Keep the following files at their declared relative paths. Apply migrations before the first query. Media and report resources must travel with the build.
| File | Purpose |
|---|---|
licenses/LICENSE.txt | Bundled asset / license |
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 CustomerCard.mcproj
mcbasic package build CustomerCard.mcproj --rid osx-arm64Acceptance checklist
- Check the UserControlLibrary project.
- Inspect both property Get/Let pairs and the OpenRequested event.
- Build its package, then follow the consumer lesson to test it in an application.
Step 7Make one change and repeat the tests#
Add a read-only summary property while preserving the existing public members.
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