McBasic™Get McBasic™ ↗
Build 881
Hands-on tutorial · Layout & Forms

Build a multi-document workspace

Host related document windows while keeping independent windows separate.

4 Form / control designs4 complete BASIC filesGuided sample project

MDI is a Form relationship, not merely a window style. The sample distinguishes child documents from an independent Form. Track each instance’s lifetime and test how closing a document differs from closing the workspace.

Step 1Prepare a safe practice project#

Extract the archive into a new writable folder, separate from your own application. Open MdiForms.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
MdiForms.mcproj
{
  "schemaVersion": 1,
  "projectId": "357c7963-827f-484a-b5a8-3714b1d6ea04",
  "name": "MdiForms",
  "kind": "DesktopApp",
  "language": {
    "dialect": "McBasic1",
    "requireExplicit": true,
    "compare": "Binary",
    "defaultArrayBase": 0
  },
  "application": {
    "bundleIdentifier": "com.mcbasic.samples.mdiforms",
    "version": "0.1.0",
    "buildNumber": "1",
    "startupFormId": "form-main",
    "shutdownMode": "OnStartupFormClosed"
  },
  "forms": [
    {
      "id": "form-main",
      "typeName": "frmMain",
      "layout": "Forms/frmMain.form.json",
      "code": "Forms/frmMain.bas"
    },
    {
      "id": "form-details",
      "typeName": "frmDetails",
      "layout": "Forms/frmDetails.form.json",
      "code": "Forms/frmDetails.bas"
    },
    {
      "id": "form-independent",
      "typeName": "frmIndependent",
      "layout": "Forms/frmIndependent.form.json",
      "code": "Forms/frmIndependent.bas"
    },
    {
      "id": "form-other",
      "typeName": "frmOther",
      "layout": "Forms/frmOther.form.json",
      "code": "Forms/frmOther.bas"
    }
  ],
  "modules": [],
  "classes": [],
  "tests": [],
  "references": [],
  "resources": [],
  "targets": [
    "osx-arm64"
  ],
  "capabilities": []
}

Step 2Build the Form and set its properties#

frmMain

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 propertyValue
Caption"MDI Forms"
Width900
Height600
MinWidth520
MinHeight480
MDIFormTrue
MDIClientTop288
TypeNameParent
LabellblIntroForm
CommandButtoncmdAlphaForm
CommandButtoncmdBetaForm
LabellblStatusForm
CommandButtoncmdCloseForm
CommandButtoncmdIndependentForm
lblIntro · exact Inspector values
PropertyValue
Left24
Top24
Width500
Height44
Caption"Two child forms share this window; another form stays independent."
Anchor edgesLeft, Top
cmdAlpha · exact Inspector values
PropertyValue
Left24
Top92
Width160
Height38
Caption"Open Alpha"
TabIndex0
Anchor edgesLeft, Top
cmdBeta · exact Inspector values
PropertyValue
Left200
Top92
Width160
Height38
Caption"Open Beta"
TabIndex1
Anchor edgesLeft, Top
lblStatus · exact Inspector values
PropertyValue
Left24
Top158
Width500
Height52
Caption"Ready. No secondary Forms are open."
Anchor edgesLeft, Top
cmdClose · exact Inspector values
PropertyValue
Left24
Top236
Width160
Height38
Caption"Close main Form"
TabIndex2
Anchor edgesLeft, Top
cmdIndependent · exact Inspector values
PropertyValue
Left376
Top92
Width160
Height38
Caption"Show independent"
TabIndex3
Anchor edgesLeft, Top

frmDetails

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 propertyValue
Caption"Details window"
Width360
Height220
MinWidth320
MinHeight190
MDIChildTrue
TypeNameParent
LabellblNameForm
CommandButtoncmdHideForm
CommandButtoncmdUnloadForm
lblName · exact Inspector values
PropertyValue
Left24
Top24
Width300
Height44
Caption"Details window"
Anchor edgesLeft, Top
cmdHide · exact Inspector values
PropertyValue
Left24
Top96
Width140
Height36
Caption"Hide window"
TabIndex0
Anchor edgesLeft, Top
cmdUnload · exact Inspector values
PropertyValue
Left176
Top96
Width140
Height36
Caption"Unload window"
TabIndex1
Anchor edgesLeft, Top

frmIndependent

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 propertyValue
Caption"Independent form"
Width420
Height420
MinWidth420
MinHeight420
TypeNameParent
LabellblNameForm
CommandButtoncmdHideForm
CommandButtoncmdUnloadForm
CommandButtoncmdCascadeForm
CommandButtoncmdTileHorizontalForm
CommandButtoncmdTileVerticalForm
CommandButtoncmdCheckActiveForm
CommandButtoncmdHideActiveForm
lblName · exact Inspector values
PropertyValue
Left24
Top24
Width300
Height44
Caption"Details window"
Anchor edgesLeft, Top
cmdHide · exact Inspector values
PropertyValue
Left24
Top96
Width140
Height36
Caption"Hide window"
TabIndex0
Anchor edgesLeft, Top
cmdUnload · exact Inspector values
PropertyValue
Left176
Top96
Width140
Height36
Caption"Unload window"
TabIndex1
Anchor edgesLeft, Top
cmdCascade · exact Inspector values
PropertyValue
Left24
Top160
Width220
Height36
Caption"Cascade"
TabIndex2
Anchor edgesLeft, Top
cmdTileHorizontal · exact Inspector values
PropertyValue
Left24
Top205
Width220
Height36
Caption"Tile horizontal"
TabIndex3
Anchor edgesLeft, Top
cmdTileVertical · exact Inspector values
PropertyValue
Left24
Top250
Width220
Height36
Caption"Tile vertical"
TabIndex4
Anchor edgesLeft, Top
cmdCheckActive · exact Inspector values
PropertyValue
Left24
Top295
Width220
Height30
Caption"Check active child"
TabIndex5
Anchor edgesLeft, Top
cmdHideActive · exact Inspector values
PropertyValue
Left24
Top340
Width220
Height32
Caption"Hide active child"
TabIndex6
Anchor edgesLeft, Top

frmOther

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 propertyValue
Caption"Other child"
Width360
Height220
MinWidth320
MinHeight190
MDIChildTrue
TypeNameParent
LabellblNameForm
CommandButtoncmdHideForm
CommandButtoncmdUnloadForm
CommandButtoncmdCancelCloseForm
CommandButtoncmdFailCloseForm
lblName · exact Inspector values
PropertyValue
Left24
Top24
Width300
Height44
Caption"Details window"
Anchor edgesLeft, Top
cmdHide · exact Inspector values
PropertyValue
Left24
Top96
Width140
Height36
Caption"Hide window"
TabIndex0
Anchor edgesLeft, Top
cmdUnload · exact Inspector values
PropertyValue
Left176
Top96
Width140
Height36
Caption"Unload window"
TabIndex1
Anchor edgesLeft, Top
cmdCancelClose · exact Inspector values
PropertyValue
Left24
Top144
Width140
Height36
Caption"Cancel next close"
TabIndex2
Anchor edgesLeft, Top
cmdFailClose · exact Inspector values
PropertyValue
Left176
Top144
Width140
Height36
Caption"Error next close"
TabIndex3
Anchor edgesLeft, Top

Step 3Connect the events explicitly#

frmMain

ObjectEventProcedureParameters
cmdAlphaClickcmdAlpha_ClickClick()
cmdBetaClickcmdBeta_ClickClick()
cmdCloseClickcmdClose_ClickClick()
cmdIndependentClickcmdIndependent_ClickClick()
FormShownForm_Shown()

frmDetails

ObjectEventProcedureParameters
cmdHideClickcmdHide_ClickClick()
cmdUnloadClickcmdUnload_ClickClick()
FormInitializeForm_Initialize()
FormLoadForm_Load()
FormShownForm_Shown()
FormQueryUnloadForm_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
FormUnloadForm_Unload()

frmIndependent

ObjectEventProcedureParameters
cmdHideClickcmdHide_ClickClick()
cmdUnloadClickcmdUnload_ClickClick()
cmdCascadeClickcmdCascade_ClickClick()
cmdTileHorizontalClickcmdTileHorizontal_ClickClick()
cmdTileVerticalClickcmdTileVertical_ClickClick()
cmdCheckActiveClickcmdCheckActive_ClickClick()
cmdHideActiveClickcmdHideActive_ClickClick()
FormInitializeForm_Initialize()
FormLoadForm_Load()
FormShownForm_Shown()
FormQueryUnloadForm_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
FormUnloadForm_Unload()

frmOther

ObjectEventProcedureParameters
cmdHideClickcmdHide_ClickClick()
cmdUnloadClickcmdUnload_ClickClick()
cmdCancelCloseClickcmdCancelClose_ClickClick()
cmdFailCloseClickcmdFailClose_ClickClick()
FormInitializeForm_Initialize()
FormLoadForm_Load()
FormShownForm_Shown()
FormQueryUnloadForm_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
FormUnloadForm_Unload()

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/frmMain.bas

Enter these declarations at file scope, before the procedures.

Forms/frmMain.bas · declarations
Option Explicit

Public ActiveAlpha As frmDetails
Public ActiveBeta As frmOther
Private Sub Form_Shown()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmMain.bas · Form_Shown
Private Sub Form_Shown()
    Dim alpha As frmDetails
    Dim beta As frmOther
    Dim active As Form
    Set alpha = New frmDetails
    Set beta = New frmOther
    Set ActiveAlpha = alpha
    Set ActiveBeta = beta
    alpha.DisplayName = "Alpha"
    beta.DisplayName = "Beta"
    Debug.Print "active-empty:" & (Me.ActiveForm Is Nothing)
    alpha.Show
    Set active = Me.ActiveForm
    Debug.Print "active-alpha:" & (active Is alpha)
    beta.Show
    Set active = Me.ActiveForm
    Debug.Print "active-beta:" & (active Is beta)
    Me.Arrange vbCascade
    frmIndependent.Show
    lblStatus.Caption = "Two MDI children and one independent window."
End Sub
Private Sub cmdAlpha_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmMain.bas · cmdAlpha_Click
Private Sub cmdAlpha_Click()
    Dim alpha As frmDetails
    Set alpha = New frmDetails
    alpha.DisplayName = "Alpha"
    alpha.Show
    lblStatus.Caption = "Another Alpha child opened inside MDI Forms."
End Sub
Private Sub cmdBeta_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmMain.bas · cmdBeta_Click
Private Sub cmdBeta_Click()
    Dim beta As frmOther
    Set beta = New frmOther
    beta.DisplayName = "Beta"
    beta.Show
    lblStatus.Caption = "Another Beta child opened inside MDI Forms."
End Sub
Private Sub cmdClose_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmMain.bas · cmdClose_Click
Private Sub cmdClose_Click()
    Unload Me
End Sub
Private Sub cmdIndependent_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmMain.bas · cmdIndependent_Click
Private Sub cmdIndependent_Click()
    frmIndependent.Show
    lblStatus.Caption = "Independent window shown outside the MDI container."
End Sub
Copy the complete Forms/frmMain.bas file

Replace the file once; do not append duplicate procedures.

Forms/frmMain.bas
Option Explicit

Public ActiveAlpha As frmDetails
Public ActiveBeta As frmOther

Private Sub Form_Shown()
    Dim alpha As frmDetails
    Dim beta As frmOther
    Dim active As Form
    Set alpha = New frmDetails
    Set beta = New frmOther
    Set ActiveAlpha = alpha
    Set ActiveBeta = beta
    alpha.DisplayName = "Alpha"
    beta.DisplayName = "Beta"
    Debug.Print "active-empty:" & (Me.ActiveForm Is Nothing)
    alpha.Show
    Set active = Me.ActiveForm
    Debug.Print "active-alpha:" & (active Is alpha)
    beta.Show
    Set active = Me.ActiveForm
    Debug.Print "active-beta:" & (active Is beta)
    Me.Arrange vbCascade
    frmIndependent.Show
    lblStatus.Caption = "Two MDI children and one independent window."
End Sub

Private Sub cmdAlpha_Click()
    Dim alpha As frmDetails
    Set alpha = New frmDetails
    alpha.DisplayName = "Alpha"
    alpha.Show
    lblStatus.Caption = "Another Alpha child opened inside MDI Forms."
End Sub

Private Sub cmdBeta_Click()
    Dim beta As frmOther
    Set beta = New frmOther
    beta.DisplayName = "Beta"
    beta.Show
    lblStatus.Caption = "Another Beta child opened inside MDI Forms."
End Sub

Private Sub cmdClose_Click()
    Unload Me
End Sub

Private Sub cmdIndependent_Click()
    frmIndependent.Show
    lblStatus.Caption = "Independent window shown outside the MDI container."
End Sub

Forms/frmDetails.bas

Enter these declarations at file scope, before the procedures.

Forms/frmDetails.bas · declarations
Option Explicit

Public DisplayName As String
Private Sub Form_Initialize()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmDetails.bas · Form_Initialize
Private Sub Form_Initialize()
    Debug.Print "initialize:" & DisplayName
End Sub
Private Sub Form_Load()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmDetails.bas · Form_Load
Private Sub Form_Load()
    Debug.Print "load:" & DisplayName
End Sub
Private Sub Form_Shown()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmDetails.bas · Form_Shown
Private Sub Form_Shown()
    lblName.Caption = "Details window: " & DisplayName
    Debug.Print "shown:" & DisplayName
End Sub
Private Sub cmdHide_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmDetails.bas · cmdHide_Click
Private Sub cmdHide_Click()
    Me.Hide
    Debug.Print "hidden:" & DisplayName
End Sub
Private Sub cmdUnload_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmDetails.bas · cmdUnload_Click
Private Sub cmdUnload_Click()
    Unload Me
End Sub
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)

This is the lifetime boundary. Preserve the cancellation signature where present and release resources owned by this Form.

Forms/frmDetails.bas · Form_QueryUnload
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    Debug.Print "query:" & DisplayName & ":" & UnloadMode
End Sub
Private Sub Form_Unload()

This is the lifetime boundary. Preserve the cancellation signature where present and release resources owned by this Form.

Forms/frmDetails.bas · Form_Unload
Private Sub Form_Unload()
    Debug.Print "unload:" & DisplayName
End Sub
Copy the complete Forms/frmDetails.bas file

Replace the file once; do not append duplicate procedures.

Forms/frmDetails.bas
Option Explicit

Public DisplayName As String

Private Sub Form_Initialize()
    Debug.Print "initialize:" & DisplayName
End Sub

Private Sub Form_Load()
    Debug.Print "load:" & DisplayName
End Sub

Private Sub Form_Shown()
    lblName.Caption = "Details window: " & DisplayName
    Debug.Print "shown:" & DisplayName
End Sub

Private Sub cmdHide_Click()
    Me.Hide
    Debug.Print "hidden:" & DisplayName
End Sub

Private Sub cmdUnload_Click()
    Unload Me
End Sub

Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    Debug.Print "query:" & DisplayName & ":" & UnloadMode
End Sub

Private Sub Form_Unload()
    Debug.Print "unload:" & DisplayName
End Sub

Forms/frmIndependent.bas

Enter these declarations at file scope, before the procedures.

Forms/frmIndependent.bas · declarations
Option Explicit

Public DisplayName As String
Private Sub Form_Initialize()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmIndependent.bas · Form_Initialize
Private Sub Form_Initialize()
    Debug.Print "initialize:" & DisplayName
End Sub
Private Sub Form_Load()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmIndependent.bas · Form_Load
Private Sub Form_Load()
    Debug.Print "load:" & DisplayName
End Sub
Private Sub Form_Shown()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmIndependent.bas · Form_Shown
Private Sub Form_Shown()
    lblName.Caption = "Details window: " & DisplayName
    Debug.Print "shown:" & DisplayName
End Sub
Private Sub cmdHide_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmIndependent.bas · cmdHide_Click
Private Sub cmdHide_Click()
    Me.Hide
    Debug.Print "hidden:" & DisplayName
End Sub
Private Sub cmdUnload_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmIndependent.bas · cmdUnload_Click
Private Sub cmdUnload_Click()
    Unload Me
End Sub
Private Sub cmdCascade_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmIndependent.bas · cmdCascade_Click
Private Sub cmdCascade_Click()
    frmMain.Arrange vbCascade
    Debug.Print "arrange:cascade"
End Sub
Private Sub cmdTileHorizontal_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmIndependent.bas · cmdTileHorizontal_Click
Private Sub cmdTileHorizontal_Click()
    frmMain.Arrange vbTileHorizontal
    Debug.Print "arrange:horizontal"
End Sub
Private Sub cmdTileVertical_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmIndependent.bas · cmdTileVertical_Click
Private Sub cmdTileVertical_Click()
    frmMain.Arrange vbTileVertical
    Debug.Print "arrange:vertical"
End Sub
Private Sub cmdCheckActive_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmIndependent.bas · cmdCheckActive_Click
Private Sub cmdCheckActive_Click()
    Debug.Print "active-alpha-now:" & (frmMain.ActiveForm Is frmMain.ActiveAlpha)
    Debug.Print "active-beta-now:" & (frmMain.ActiveForm Is frmMain.ActiveBeta)
    Debug.Print "active-none-now:" & (frmMain.ActiveForm Is Nothing)
End Sub
Private Sub cmdHideActive_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmIndependent.bas · cmdHideActive_Click
Private Sub cmdHideActive_Click()
    If Not (frmMain.ActiveForm Is Nothing) Then
        frmMain.ActiveForm.Hide
        Debug.Print "hide-active"
    End If
End Sub
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)

This is the lifetime boundary. Preserve the cancellation signature where present and release resources owned by this Form.

Forms/frmIndependent.bas · Form_QueryUnload
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    Debug.Print "query:" & DisplayName & ":" & UnloadMode
End Sub
Private Sub Form_Unload()

This is the lifetime boundary. Preserve the cancellation signature where present and release resources owned by this Form.

Forms/frmIndependent.bas · Form_Unload
Private Sub Form_Unload()
    Debug.Print "unload:" & DisplayName
End Sub
Copy the complete Forms/frmIndependent.bas file

Replace the file once; do not append duplicate procedures.

Forms/frmIndependent.bas
Option Explicit

Public DisplayName As String

Private Sub Form_Initialize()
    Debug.Print "initialize:" & DisplayName
End Sub

Private Sub Form_Load()
    Debug.Print "load:" & DisplayName
End Sub

Private Sub Form_Shown()
    lblName.Caption = "Details window: " & DisplayName
    Debug.Print "shown:" & DisplayName
End Sub

Private Sub cmdHide_Click()
    Me.Hide
    Debug.Print "hidden:" & DisplayName
End Sub

Private Sub cmdUnload_Click()
    Unload Me
End Sub

Private Sub cmdCascade_Click()
    frmMain.Arrange vbCascade
    Debug.Print "arrange:cascade"
End Sub

Private Sub cmdTileHorizontal_Click()
    frmMain.Arrange vbTileHorizontal
    Debug.Print "arrange:horizontal"
End Sub

Private Sub cmdTileVertical_Click()
    frmMain.Arrange vbTileVertical
    Debug.Print "arrange:vertical"
End Sub

Private Sub cmdCheckActive_Click()
    Debug.Print "active-alpha-now:" & (frmMain.ActiveForm Is frmMain.ActiveAlpha)
    Debug.Print "active-beta-now:" & (frmMain.ActiveForm Is frmMain.ActiveBeta)
    Debug.Print "active-none-now:" & (frmMain.ActiveForm Is Nothing)
End Sub

Private Sub cmdHideActive_Click()
    If Not (frmMain.ActiveForm Is Nothing) Then
        frmMain.ActiveForm.Hide
        Debug.Print "hide-active"
    End If
End Sub

Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    Debug.Print "query:" & DisplayName & ":" & UnloadMode
End Sub

Private Sub Form_Unload()
    Debug.Print "unload:" & DisplayName
End Sub

Forms/frmOther.bas

Enter these declarations at file scope, before the procedures.

Forms/frmOther.bas · declarations
Option Explicit

Public DisplayName As String
Private CancelNextClose As Boolean
Private FailNextClose As Boolean
Private Sub Form_Initialize()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmOther.bas · Form_Initialize
Private Sub Form_Initialize()
    Debug.Print "initialize:" & DisplayName
End Sub
Private Sub Form_Load()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmOther.bas · Form_Load
Private Sub Form_Load()
    Debug.Print "load:" & DisplayName
End Sub
Private Sub Form_Shown()

Initialize state before enabling dependent actions. Keep the shared declarations and startup dependencies with this procedure.

Forms/frmOther.bas · Form_Shown
Private Sub Form_Shown()
    lblName.Caption = "Details window: " & DisplayName
    Debug.Print "shown:" & DisplayName
End Sub
Private Sub cmdHide_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmOther.bas · cmdHide_Click
Private Sub cmdHide_Click()
    Me.Hide
    Debug.Print "hidden:" & DisplayName
End Sub
Private Sub cmdUnload_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmOther.bas · cmdUnload_Click
Private Sub cmdUnload_Click()
    Unload Me
End Sub
Private Sub cmdCancelClose_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmOther.bas · cmdCancelClose_Click
Private Sub cmdCancelClose_Click()
    CancelNextClose = True
    lblName.Caption = "Next close will be cancelled."
End Sub
Private Sub cmdFailClose_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmOther.bas · cmdFailClose_Click
Private Sub cmdFailClose_Click()
    FailNextClose = True
    lblName.Caption = "Next close will report an error."
End Sub
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)

This is the lifetime boundary. Preserve the cancellation signature where present and release resources owned by this Form.

Forms/frmOther.bas · Form_QueryUnload
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    Debug.Print "query:" & DisplayName & ":" & UnloadMode
    If CancelNextClose Then
        CancelNextClose = False
        Cancel = 1
        Debug.Print "cancel-close:" & DisplayName
    End If
    If FailNextClose Then
        FailNextClose = False
        Debug.Print "error-close:" & DisplayName
        Err.Raise 513, "MdiForms", "Requested child close failure"
    End If
End Sub
Private Sub Form_Unload()

This is the lifetime boundary. Preserve the cancellation signature where present and release resources owned by this Form.

Forms/frmOther.bas · Form_Unload
Private Sub Form_Unload()
    Debug.Print "unload:" & DisplayName
End Sub
Copy the complete Forms/frmOther.bas file

Replace the file once; do not append duplicate procedures.

Forms/frmOther.bas
Option Explicit

Public DisplayName As String
Private CancelNextClose As Boolean
Private FailNextClose As Boolean

Private Sub Form_Initialize()
    Debug.Print "initialize:" & DisplayName
End Sub

Private Sub Form_Load()
    Debug.Print "load:" & DisplayName
End Sub

Private Sub Form_Shown()
    lblName.Caption = "Details window: " & DisplayName
    Debug.Print "shown:" & DisplayName
End Sub

Private Sub cmdHide_Click()
    Me.Hide
    Debug.Print "hidden:" & DisplayName
End Sub

Private Sub cmdUnload_Click()
    Unload Me
End Sub

Private Sub cmdCancelClose_Click()
    CancelNextClose = True
    lblName.Caption = "Next close will be cancelled."
End Sub

Private Sub cmdFailClose_Click()
    FailNextClose = True
    lblName.Caption = "Next close will report an error."
End Sub

Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    Debug.Print "query:" & DisplayName & ":" & UnloadMode
    If CancelNextClose Then
        CancelNextClose = False
        Cancel = 1
        Debug.Print "cancel-close:" & DisplayName
    End If
    If FailNextClose Then
        FailNextClose = False
        Debug.Print "error-close:" & DisplayName
        Err.Raise 513, "MdiForms", "Requested child close failure"
    End If
End Sub

Private Sub Form_Unload()
    Debug.Print "unload:" & DisplayName
End Sub

Step 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:

Developer CLI
mcbasic check MdiForms.mcproj
mcbasic run MdiForms.mcproj

Acceptance checklist

  • Open two document Forms in the workspace.
  • Open the independent Form and compare its behavior.
  • Hide and unload a document, then reopen it.
  • Close the workspace and inspect the resulting application lifetime.

Step 7Make one change and repeat the tests#

Give each document a distinct public title before showing it.

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.

Source baseline 20f4bfed6b · Build manifest