devxlogo

Avoiding MDI: Making a form a child of another form

Avoiding MDI: Making a form a child of another form

Question:
How do I make a form a child of another form without having to resort to the overhead of MDI?

Answer:

MDI provides an easy and well-established way for developers to create applications in which one form can contain several forms in a Parent-Child relationship. But because MDI is so well-defined, it takes up a lot of resources. Especially in the case where you want to create a child form for the express purpose of being a tool bar of sorts, carrying the MDI along for just this purpose can be a bit of a waste of resources. But there is a work-around for it.

All Delphi forms inherit a very useful method from TWinControl called CreateParams. You can override this method to achieve various behaviors in the way a form diplays when it’s created. Without going into a lot of esoteric Windows API lingo, CreateParams initializes a TCreateParams structure that contains generic base values defining a window’s appearance. The record structure is listed below:

type    TCreateParams = record    Caption: PChar;    Style: Longint;    ExStyle: Longint;    X, Y: Integer;    Width, Height: Integer;    WndParent: HWND;    Param: Pointer    WindowClass: TWndClass;    WinClassName: array[0..63] of Char;  end;

We won’t go into any detail about this structure because it’s beyond the scope of this discussion. I’ll leave it up to you to peruse the online help for a detailed discussion of the topic. However, note that there are several things you can change in the structure itself. With respect to our current discussion, to achieve our desired result of having a form be the child of another form, we’ll manipulate this structure within our CreateParams method.

It’s actually a fairly trivial thing to make a form act as a child form. All we have to do is the following:

  1. Create the form with a Child window style
  2. Notify Windows that the form has a new parent
  3. Notify Delphi that the form has a new parent

It couldn’t get easier than this. Here’s the code for the method that accomplishes the steps above:

unit child;interfaceuses  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;type  TChildForm = class(TForm)    procedure FormClose(Sender: TObject; var Action: TCloseAction);    procedure Button1Click(Sender: TObject);  private    procedure CreateParams(var Params : TCreateParams); override;  public    { Public declarations }  end;var  ChildForm: TChildForm;implementation{$R *.DFM}procedure TChildForm.CreateParams(var Params : TCreateParams);begin  inherited CreateParams(Params);  with Params do begin    {Set the Child’s style to WS_CHILD}    Style := (Style OR WS_CHILD) AND NOT WS_POPUP;    X := 50;    Y := 50;    Width := 200;    Height := 150;    {Let Windows know about the relationship}    WndParent := Application.MainForm.Handle;  end;  {Let Delphi know about the relationship}  Parent := Application.MainForm;end;procedure TChildForm.FormClose(Sender: TObject; var Action: TCloseAction);begin  Action := caFree;end;procedure TChildForm.Button1Click(Sender: TObject);begin  ShowMessage(‘Hello world!!!’);end;end.

Nothing really complex about that, right? All I did with the TCreateParams structure was to set the window style to WS_CHILD so the window is created a child, then set the parent-child relationship with a couple of assignment lines. With this sample, I simply embedded a TButton on the form to display the obligatory test message Hello world!!! But you can make the form do practically anything you want. Also, note the FormClose method: I’ve set it so that the form is destroyed when it is closed. However, you can substitute the Action := caFree; with Action := caMinimize to mimic the behavior of an MDIChild form.

Since this is a fairly simple tip, I didn’t include a sample application. However, if you want to create a test application to test this concept out, do the following:

  1. Create a new application in Delphi.
  2. Name the main form something like ParentForm to differentiate it from other forms.
  3. Using either the tool bar or File|New Form, create a new form.
  4. Insert the code above in the Child form as above.
  5. Insert a uses statement in the implementation of your MainForm and declare your child form’s unit name.

Create a child form like this as you would normally create any other form. Here’s some sample code from the main form of my test application:

procedure TForm1.Button1Click(Sender: TObject);var  ChildFrm : TChildForm;begin  ChildFrm := TChildForm.Create(Application);  ChildFrm.Show;end;

This is plain vanilla form creation, so there’s nothing here that you don’t already know how to do. In fact, if you look at all the code above, there’s really nothing so complex that you couldn’t have figured it out sooner or later by yourself.

You might be asking Of what use is this methodology? Well, besides toolbars, I use this technique for creating wizard-like programs. I also use it instead of using notebooks because sometimes my pages can get fairly complex. So to reduce resource utilization, I instead create a bunch of child forms that don’t have any caption bars or borders and are maximized in the client area of the main form. As the user goes from page to page, the components embedded on the pages are created and destroyed on the fly. This may be a bit slower in comparison to having a notebook, but this technique is far less resource-intensive than using a notebook, which would load up all the components at run time.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist