Design control arrays & shared events
Build repeated controls with one name, address a particular member and handle its events without duplicating procedures.
A name identifies the group; Index identifies the member#
A control array contains same-type visual controls with one Name and distinct Index values. Set the group name to txtCell, not txtCell(0); put the number in the separate Index field. The Designer and project tree display the combined member identity. A plain BASIC array of TextBox references is different: it points to existing controls and does not create any controls.
| Rule | Meaning |
|---|---|
| Index | An integer from 0 to 32767, unique within this named array. |
| Type | Every member has the same standard visual control type. |
| Membership | Every control sharing that name has an Index; even a one-member array needs indexed access. |
| Gaps | Allowed. Indexes 2, 5 and 9 describe three members, not ten. |
| Parent | Members may have different containers on the same Form. |
Choose a control whose metadata declares the event you need. Label, Image and Panel now support Click; array membership adds Index to the existing event signature.
Create the grid before writing its behavior#
Select one eligible, non-array visual control on a Form. Choose Design → Create Control Array…, the matching Arrange command in the Design ribbon, or the control’s context-menu command. The selected control is the template and becomes the first member; it is not left behind as a separate control.
| Field | Value / effect |
|---|---|
| Columns | 1–32 members per row. |
| Rows | 1–32 rows. |
| Spacing | 0–1000 points between adjacent members. |
| Group every | 0 disables grouping; otherwise adds a wider gap after each group of rows and columns. |
| Group spacing | 0–1000 extra points at group boundaries. |
| Start Index | First member number; later members count across each row, then down. The final number must not exceed 32767. |
Members inherit template properties and advance its TabIndex. The operation must leave the Form with no more than 2048 controls and components. Invalid values leave the project unchanged. Existing bound event headers receive Index as their first parameter in the same Undo step; a direct call to an old handler or other now-invalid source can prevent creation. Resolve the reported code issue first.
Practice with the complete six-cell board lesson, including exact dimensions, all event code and runtime checks.
One event procedure, one incoming member number#
Private Sub txtCell_Change(ByVal Index As Integer)
lblStatus.Caption = "Cell " & Index & ": " & txtCell(Index).Text
End SubIndex comes before the event’s existing parameters. Use Integer or Long, passed by value; a bare Index parameter is accepted for a synchronous array handler. Async handlers require explicit ByVal Index As Integer and an event that already supports async. They are not a way to make arbitrary parameterized events asynchronous.
Private Sub txtCell_KeyPress(ByVal Index As Integer, ByVal Character As String, ByRef Handled As Boolean)
If txtCell(Index).Locked Then Handled = True
End SubFor ListView ItemClick and ItemDoubleClick, distinguish the leading control-array Index from the event’s own item position, conventionally named ItemIndex. Double-clicking a member opens its shared default handler. The component pages show the ordinary single-control signature; an array handler adds Index before it.
Read members, not assumed positions#
| BASIC expression | Contract |
|---|---|
txtCell(i) or txtCell.Item(i) | The member whose Index is i; absent members raise error 340. |
txtCell.Count | The number of existing members, including hidden ones. |
txtCell.LBound, txtCell.UBound | Lowest and highest existing Index, not proof that intermediate members exist. |
LBound(txtCell), UBound(txtCell) | Intrinsic bound forms are also supported. |
For Each cell In txtCell | Visits existing members in increasing Index order; declare cell As TextBox or As Control. |
Prefer For Each when gaps are possible. A counted loop from LBound to UBound can ask for a deleted member. Hiding a member changes Visible, not membership or Count. Programmatic Text changes also raise Change; guard bulk updates with a Boolean flag as the lesson demonstrates.
Understand promotion, paste, rename and Undo#
| Action | What to expect |
|---|---|
| Paste or duplicate a matching single control | Yes promotes the existing control and the copy to an array and adapts event headers; No gives the copy a new name without changing code. |
| Paste a member of an existing array | The copy receives the next free Index and retains the shared event binding. |
| Rename a control to an existing same-type name | A confirmation can join or create the array. No leaves the name unchanged. |
| Rename an array member | Choose Rename Array, Take Out of Array or Cancel. Taking out one member removes its Index and event bindings. |
| Set Index directly in Properties | Membership changes, but handler source is not rewritten for you. Correct the signature and run Check. |
| Delete a member | Only that member is removed; remaining Index values are not renumbered. |
Promotion and joining are checked before applying changes. If a message begins with MCB4603, read the explanation of the code that must change; do not repeatedly paste Yes into an already failing project. A successful Create, paste promotion or rename is one Undo step covering the related layout, headers and target updates.
Point services at a member#
ContextMenu and ValidationProvider Target values select a specific member, for example txtCell(4). The bare group name is not a valid Target. Promotion of a plain control updates a matching Target to its first member. Deleting a targeted control leaves the ContextMenu items but clears its Target; choose a surviving member before Check or Run.
Create and remove a runtime member#
From build 843, Load txtCell(6) creates a member from the saved design-time template with the lowest Index. It begins hidden; set position and state before Visible True. The owning Form must perform Load with an unused Index. Unload removes runtime-created members only. The Modern Workbench includes both buttons, guards, bindings and complete handlers.
| Error | Cause / recovery |
|---|---|
| 340 | Missing or invalid Index; address an existing member. |
| 360 | Index already loaded; choose an unused Index. |
| 362 | Cannot unload a design-time member; hide it instead. |
| 365 | Cannot unload a member during its own event; defer or use another control. |
Count and bounds reflect membership. For Each over a dynamically loaded group uses a snapshot at loop entry. Adding a member does not extend that iteration. A removed control is no longer usable.
Give the AI the intended array boundary#
Right-click a member and choose Add Array to Chat when the assistant must edit the group and its shared event procedures. Ordinary Add to Chat on one member does not grant write access to a handler shared by the others. Adding a new Index remains outside the existing array scope and requires permission.
The array chip follows the array name, not a permanent group ID. After renaming the array, add it to scope again and review the chips before sending. AI grid generation uses one addControlArray operation, but still obeys member limits and requires complete handler code. See scope and permissions.
20f4bfed6b · Build manifestSources & maintenance
Original web explanation, reviewed against these repository sources. API tables and example files follow the implementation.
docs/manual/05-designing-forms.mddocs/manual/06-writing-mcbasic.mddocs/manual/21-language-reference.mdsrc/McBasic.Workspace/ControlArrayRules.cssrc/McBasic.Runtime.UI/McControlArray.csmacos/McBasicIDE/Sources/ControlArrays.swiftmacos/McBasicIDE/Sources/CreateControlArraySheet.swifttests/McBasic.Compiler.Tests/ControlArrayLoadChecks.cs