In this chapter, we are going to create an MDI image viewer application similar to the one displayed in Figure 17-1.
As you'll see in a moment, it's actually really easy to build MDI applications in Delphi, especially if you already know how to dynamically create modeless forms.
The first thing that you have to do, after you've created a new VCL Forms application project, is create the MDI parent form by setting the FormStyle property of the form to fsMDIForm.
Now that you've created the MDI parent form, you have to create the MDI child form by doing the following:
Add a new empty form to the project (File ® New ® Form - Delphi for Win32).
Set the FormStyle property of the form to fsMDIChild.
Optionally rename the form ChildForm or something similar.
If you run the application now, you'll see that the child form is automatically displayed inside the parent form.
Figure 17-4: The child form
To finish the creation of the child form, we have to remove it from the auto-create list (Project ® Options). This way, the child form will not be automatically displayed when the application starts.
Also, we have to handle the form's OnClose event and tell the form to free itself when it's closed:
procedure TChildForm.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end;
We have to do this because, by default, child forms only get minimized when you try to close them, as shown in Figure 17-5.
Figure 17-5: Child forms are, by default, only minimized when you try to close them.
To display an image on the child window, add a TImage component to the child form and then do the following:
Set the Top and Left properties to 0 to move the image component into the top-left corner of the form.
Set the AutoSize property to True to enable the TImage component to resize itself when we load an image.
Switch to the main form, add a TMainMenu component to the form, and then create the File ® Open menu item to enable the user to select one or more images and open them.
Note | Besides bitmaps, icons, and metafiles, Delphi can also open JPEG images. To enable an application to work with JPEG images, you simply have to add jpeg to the uses list. |
In the OnClick event handler of the Open item, we have to do the following:
Display the TOpenDialog dialog box to enable the user to select images.
Create an instance of the child form for every selected image.
Load the selected images.
First, let's create the TOpenDialog component dynamically and set its properties in code. To enable the user to select multiple items (in this case, multiple images), we have to create a filter that only displays image files in the dialog box and we have to include ofAllowMultiSelect in the Options set. Including ofAllowMultiSelect enables you to select multiple items and stores the selected items in the Files property.
Listing 17-1: Creating a common open dialog box that allows the user to select multiple files
procedure TMainForm.OpenItemClick(Sender: TObject); var cnt: Integer; begin with TOpenDialog.Create(Self) do try Filter := 'Images (*.bmp, *.jpg)|*.bmp;*.jpg;*.jpeg'; Options := Options + [ofAllowMultiSelect]; finally Free; end; // try TOpenDialog.Create end;
If the user clicks OK in the dialog box, we have to read the file names from the Files property and create a child form instance for each file (see Listing 17-2A for the Delphi version and Listing 17-2B for the C++ version).
Listing 17-2A: Creating child instances and loading selected images, Delphi version
procedure TMainForm.OpenItemClick(Sender: TObject); var cnt: Integer; begin with TOpenDialog.Create(Self) do try Filter := 'Images (*.bmp, *.jpg)|*.bmp;*.jpg;*.jpeg'; Options := Options + [ofAllowMultiSelect]; if Execute then for cnt := 0 to Pred(Files.Count) do with TChildForm.Create(Self) do begin // enable AutoSize temporarily to resize the window // to fit the loaded image AutoSize := True; // load the selected image Caption := ExtractFileName(Files[cnt]); Image1.Picture.LoadFromFile(Files[cnt]); // disable AutoSize so that we can resize the window AutoSize := False; end; // with TChildForm finally Free; end; // try TOpenDialog.Create end;
Listing 17-2: B Creating child instances and loading selected images, C++ version
void __fastcall TMainForm::OpenItemClick(TObject *Sender) { AnsiString fName; TOpenDialog* dialog = new TOpenDialog(this); try { dialog->Filter = "Images (*.bmp, *.jpg)|*.bmp;*.jpg;*.jpeg"; dialog->Options << ofAllowMultiSelect; if(dialog->Execute()) { for(int cnt = 0; cnt < dialog->Files->Count; cnt++) { fName = dialog->Files->Strings[cnt]; // don't forget to include the childform's unit // File -> Include Unit Hdr... TChildForm* frm = new TChildForm(this); frm->AutoSize = true; frm->Caption = ExtractFileName(fName); frm->Image1->Picture->LoadFromFile(fName); // disable AutoSize frm->AutoSize = false; } } } __finally { delete dialog; } }
MDI applications usually have a Window menu that contains commands like Tile Vertically, Tile Horizontally, Cascade, Arrange Icons, Minimize All, and Close All for managing child windows. The Window menu can also automatically display a list of all opened child windows, as shown in Figure 17-6. To have the Window menu automatically display the list of all opened child windows, assign the actual Window menu item to the WindowMenu property of the form.
Figure 17-6: The Window menu
The implementation of all Window menu commands is displayed in Listing 17-3.
Listing 17-3: The Window menu commands
procedure TMainForm.CloseAllItemClick(Sender: TObject); var cnt: Integer; begin for cnt := Pred(MDIChildCount) downto 0 do MDIChildren[cnt].Close; end; procedure TMainForm.ArrangeIconsItemClick(Sender: TObject); begin ArrangeIcons; end; procedure TMainForm.CascadeItemClick(Sender: TObject); begin Cascade; end; procedure TMainForm.TileHorizontallyItemClick(Sender: TObject); begin TileMode := tbHorizontal; Tile; end; procedure TMainForm.TileVerticallyItemClick(Sender: TObject); begin TileMode := tbVertical; Tile; end; procedure TMainForm.MinimizeAllItemClick(Sender: TObject); var cnt: Integer; begin for cnt := Pred(MDIChildCount) downto 0 do MDIChildren[cnt].WindowState := wsMinimized; end;