Public Parent As Form
Now you have somewhere to store a reference to the parent form, so you need to arrange for this reference to be set when the form is loaded. Since you can't pass parameters to a form's Show method (or to the CFormAttributes instance), you need to do this manually from outside the CFormAttributes class. You want to be able to do something like this:
Public Sub ShowChild Child:=frmReview, Parent:=Me
You could make this a global procedure and give it its own BAS file, but it's better to keep all the code in one place by making ShowChild a method of the CFormAttributes class. Obviously, this means you can't invoke ShowChild to display the first form in a hierarchy, but the only implication of this is that you need to make sure that the CFormAttributes class recognizes that it has no parent when it is destroyed. You can also dispense with the Parent parameter since you already have a Self reference in the CFormAttributes class. Here's the method in the CFormAttributes class, which is named NewChild:
Public Sub NewChild(ByVal frmiChild As Form) frmiChild.Show Set frmiChild.My.Parent = frmPiSelf frmPiSelf.Enabled = False End Sub
The last statement is the significant one because it's the one that disables the parent form and creates a new mode. You need a reciprocal action to reenable the parent when the form unloads, so you need to define another method:
Public Sub EnableParent() If Not Me.Parent Is Nothing Then Me.Parent.Enabled = True End Sub
Unfortunately, there's no elegant way to bind this to a form unload; you must ensure that you call this method from each Form_Unload event:
Private Sub Form_Unload() My.EnableParent End Sub
In fact, the sample code in CHAP13\atribcls\pubatrib.cls has a generic UnloadActions method, which takes the place of EnableParent, but this discussion is clearer if I continue to refer to an EnableParent method.
That takes care of modal child forms, as long as you invoke them with My.NewChild and include the appropriate reciprocal call in the Form_Unload event. You can now build on this to extend the mechanism. To cope with the swapping in the sample program, for example, you need to do a couple of extra things: pass on the outgoing form's parent reference to the new form and then prevent the parent from being reenabled when the old form unloads. You can do this by adding a new method and modifying the EnableParent method slightly so that the two communicate through a module-level flag:
Private bPiKeepParentDisabled As Boolean Public Sub SwapMe(ByVal frmiNewChild As Form) frmiNewChild.Show vbModeless If frmiNewChild.Enabled Then Set frmiNewChild.My.Parent = Parent bPiKeepParentDisabled = True End If Unload frmPiSelf End Sub Public Sub EnableParent() If Not bPiKeepParentDisabled Then If Not Parent Is Nothing Then Parent.Enabled = True End If End Sub
Notice the check to find out whether the form you're trying to swap to is enabled. If it isn't, it must already have been loaded, in which case you'll just leave the Parent property alone. This is an ad hoc test that works in the simple examples shown here, but it might not be general, and so you'll need to extend the mechanism to cope with other situations. For example, the mechanism as it stands won't prevent you from trying to swap to a form that's in the middle of a modal cascade-in fact, this would orphan any child forms in the cascade. With a little thought, you should be able to extend the mechanism to allow swapping to remove child forms of the form you're trying to swap to, to prevent swapping between forms belonging to other functions in a function modal situation, or to support any other flavors of modality you care to invent.