Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents
Bookmark and Share

Part I - Foundations
  Chapter 1 Delphi 7 and Its IDE
  Chapter 2 The Delphi Programming Language
  Chapter 3 The Run-Time Library
  Chapter 4 Core Library classes
  Chapter 5 Visual Controls
  Chapter 6 Building the User Interface
  Chapter 7 Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 The Architecture of Delphi Applications
  Chapter 9 Writing Delphi Components
  Chapter 10 Libraries and Packages
  Chapter 11 Modeling and OOP Programming (with ModelMaker)
  Chapter 12 From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 Delphi's Database Architecture
  Chapter 14 Client/Server with dbExpress
  Chapter 15 Working with ADO
  Chapter 16 Multitier DataSnap Applications
  Chapter 17 Writing Database Components
  Chapter 18 Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 Internet Programming: Sockets and Indy
  Chapter 20 Web Programming with WebBroker and WebSnap
  Chapter 21 Web Programming with IntraWeb
  Chapter 22 Using XML Technologies
  Chapter 23 Web Services and SOAP
  Chapter 24 The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 Delphi for .NET Preview: The Language and the RTL
  Appendix A Extra Delphi Tools by the Author
  Appendix B Extra Delphi Tools from Other Sources
  Appendix C Free Companion Books on Delphi
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

Previous Section Next Section

Frame and Child Windows in Delphi

Delphi makes it easy to develop MDI applications, even without using the MDI Application template available in Delphi (see the Applications page of the File ® New ® Other dialog box). You only need to build at least two forms, one with the FormStyle property set to fsMDIForm and the other with the same property set to fsMDIChild. Set these two properties in a simple program and run it, and you'll see the two forms nested in the typical MDI style.

Generally, however, the child form is not created at startup, and you need to provide a way to create one or more child windows. You can do so by adding a menu with a New menu item and writing the following code:

  ChildForm: TChildForm;
  ChildForm := TChildForm.Create (Application);

Another important feature to add is a Window pull-down menu, which you use as the value of the form's WindowMenu property. This pull-down menu will automatically list all the available child windows. (Of course, you can choose any other name for the pull-down menu, but Window is the standard.)

To make this program work properly, you can add a number to the title of any child window when it is created:

procedure TMainForm.New1Click(Sender: TObject);
  ChildForm: TChildForm;
  WindowMenu := Window1;
  Inc (Counter);
  ChildForm := TChildForm.Create (Self);
  ChildForm.Caption := ChildForm.Caption + ' ' + IntToStr (Counter);

You can also open child windows, minimize or maximize each of them, close them, and use the Window pull-down menu to navigate among them. Now suppose you want to close some of these child windows, to unclutter the client area of your program. Click the Close boxes in some of the child windows, and they are minimized! What is happening? Remember that when you close a window, you generally hide it from view. The closed forms in Delphi still exist, although they are not visible. In the case of child windows, hiding them won't work, because the MDI Window menu and the list of windows will still list existing child windows, even if they are hidden. For this reason, Delphi minimizes the MDI child windows when you try to close them. To solve this problem, you need to delete the child windows when they are closed by setting the Action reference parameter of the OnClose event to caFree.

Building a Complete Window Menu

Your first task is to define a better menu structure for the example. Typically the Window pull-down menu has at least three items: Cascade, Tile, and Arrange Icons. To handle the menu commands, you can use some of the predefined methods of TForm that can be used only for MDI frames:

Cascade Method  Cascades the open MDI child windows. The windows overlap each other. Iconized child windows are also arranged (see ArrangeIcons).

Tile Method  Tiles the open MDI child windows; the child forms are arranged so that they do not overlap. The default behavior is horizontal tiling, although if you have several child windows, they will be arranged in several columns. This default can be changed by using the TileMode property (set the value to either tbHorizontal or tbVertical).

ArrangeIcons Procedure  Arranges all the iconized child windows. Open forms are not moved.

As a better alternative to calling these methods, you can place an ActionList in the form and add to it a series of predefined MDI actions. The related classes are TWindowArrange, TWindowCascade, TWindowClose, TWindowTileHorizontal, TWindowTileVertical, and TWindowMinimizeAll. The connected menu items will perform the corresponding actions and will be disabled if no child window is available. The MdiDemo example, which we'll look at next, demonstrates the use of the MDI actions, among other things.

There are some other interesting methods and properties related strictly to MDI in Delphi:

ActiveMDIChild Property  A run-time, read-only property of the MDI frame form that holds the active child window. The user can change this value by selecting a new child window, or the program can change it using the Next and Previous procedures, which activate the child window following or preceding the currently active one.

ClientHandle Property  Holds the Windows handle of the MDI client window, which covers the client area of the main form.

MDIChildren Property  An array of child windows you can use together with the MDIChildCount property to cycle among all the child windows. This property can be useful for finding a particular child window or to operate on each of them.

Note that the internal order of the child windows is the reverse order of activation. This means the last child window selected is the active window (the first in the internal list), the second-to-last child window selected is the second, and the first child window selected is the last. This order determines how the windows are arranged on the screen. The first window in the list is above all the others, whereas the last window is below all the others, and probably hidden. You can imagine an axis (the z-axis) coming out of the screen toward you. The active window has a higher value for the z coordinate and, thus, covers other windows. For this reason, the Windows ordering schema is known as the z-order.


The Window menu can be used along with the ActionManager and the ActionMainMenuBar control hosting the menu, starting with Delphi 7. This control has a specific property, WindowMenu, that you have to set to specify the menu that is going to list the MDI child windows.

The MdiDemo Example

I've built an example to demonstrate most of the features of a simple MDI application. MdiDemo is a full-blown MDI text editor, because each child window hosts a Memo component and can open and save text files. The child form has a Modified property that indicates whether the text of the memo has changed (it is set to True in the handler of the memo's OnChange event). Modified is set to False in the Save and Load custom methods and is checked when the form is closed (prompting the user to save the file).

As I've mentioned, the example's main form is based on an ActionList component. The actions are available through some menu items and a toolbar, as shown in Figure 8.3. You can see the details of the ActionList in the example's source code; I'll focus on the code of the custom actions.

Click To expand
Figure 8.3: The MdiDemo program uses a series of predefined Delphi actions connected to a menu and a toolbar.

One of the simplest actions is the ActionFont object, which has both an OnExecute handler (which uses a FontDialog component) and an OnUpdate handler (which disables the action—and hence the associated menu item and toolbar button—when there are no child forms):

procedure TMainForm.ActionFontExecute(Sender: TObject);
  if FontDialog1.Execute then
    (ActiveMDIChild as TChildForm).Memo1.Font := FontDialog1.Font;
procedure TMainForm.ActionFontUpdate(Sender: TObject);
  ActionFont.Enabled := MDIChildCount > 0;

The action named New creates the child form and sets a default filename. The Open action calls the ActionNewExcecute method prior to loading the file:

procedure TMainForm.ActionNewExecute(Sender: TObject);
  ChildForm: TChildForm;
  Inc (Counter);
  ChildForm := TChildForm.Create (Self);
  ChildForm.Caption :=
    LowerCase (ExtractFilePath (Application.Exename)) + 'text' +
    IntToStr (Counter) + '.txt';
procedure TMainForm.ActionOpenExecute(Sender: TObject);
  if OpenDialog1.Execute then
    ActionNewExecute (Self);
    (ActiveMDIChild as TChildForm).Load (OpenDialog1.FileName);

The file loading is performed by the form's Load method. Likewise, the child form's Save method is used by the Save and Save As actions. Notice the Save action's OnUpdate handler, which enables the action only if the user has changed the memo's text:

procedure TMainForm.ActionSaveAsExecute(Sender: TObject);
  // suggest the current file name
  SaveDialog1.FileName := ActiveMDIChild.Caption;
  if SaveDialog1.Execute then
    // modify the file name and save
    ActiveMDIChild.Caption := SaveDialog1.FileName;
    (ActiveMDIChild as TChildForm).Save;
procedure TMainForm.ActionSaveUpdate(Sender: TObject);
  ActionSave.Enabled := (MDIChildCount > 0) and
    (ActiveMDIChild as TChildForm).Modified;
procedure TMainForm.ActionSaveExecute(Sender: TObject);
  (ActiveMDIChild as TChildForm).Save;

Previous Section Next Section



Copyright © 2004-2016 "Delphi Sources". Delphi Programming Guide
     Twitter     Facebook