I. Introduction▲
Dans cet exemple, nous allons suivre pas à pas la construction d'une application MDI. Notre application ne fera rien de particulier si ce n'est de gérer ses fenêtres enfants. Si vous suivez ce tutoriel pas à pas, vous devrez obtenir une application ressemblant à la capture d'écran ci-dessous :
Tout d'abord il faut créer une nouvelle application, nous reviendrons plus tard sur sa fiche principale Form1. Construisons en premier lieu le modèle de la fiche enfant (Form2).
II. Construction de la fiche enfant MDI▲
Création de la fiche enfant (Form2) : sur le menu faire : « Fichier » puis « Nouveau » puis « Fiche », mettre sa propriété FormStyle à fsMDIChild.
Cette fiche ne doit pas être créée automatiquement au démarrage de l'application, nous devons donc la passer en fiche disponible : sur le menu faire « Projet » puis « Options… » puis sur la boîte de dialogue sélectionner l'onglet « Fiches » puis dans la liste de gauche (Fiches créées automatiquement) sélectionner Form2, appuyer sur le bouton « > », Form2 doit passer sur la liste de droite (Fiches disponibles). Si vous consultez le code source de project1.cpp vous remarquerez que cette ligne : Application->CreateForm(__classid(TForm2), &Form2); a disparu. Supprimer cette ligne manuellement donne donc le même résultat.
Pour agrémenter cette fiche, nous y placerons un TMemo avec sa propriété Align à alClient. Nous allons maintenant aborder le code de la fiche enfant
Unit2.h : nous y déclarons une donnée membre static « Numero », elle existera donc en un seul exemplaire, quel que soit le nombre d'instances de la classe qui seront créées. Elle nous servira justement à compter ces instances pour leur donner un numéro que l'on affichera sur leur « Caption » :
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm2 : public TForm
{
__published
:
// Composants gérés par l'EDI
TMemo *
Memo1;
void
__fastcall FormClose
(
TObject *
Sender, TCloseAction &
Action);
void
__fastcall FormCreate
(
TObject *
Sender);
private
:
// Déclarations de l'utilisateur
static
int
Numero;
public
:
// Déclarations de l'utilisateur
__fastcall TForm2
(
TComponent*
Owner);
}
;
//---------------------------------------------------------------------------
extern
PACKAGE TForm2 *
Form2;
//---------------------------------------------------------------------------
#endif
Unit2.cpp : dans le constructeur, on incrémente la donnée membre static « Numero » à chaque fois que l'on créera une nouvelle fiche enfant et on ajoutera ce numéro à son « Caption » sur l'événement OnCreate de la fiche. On pourrait aussi par exemple l'affecter à la propriété tag de la fiche pour avoir un numéro unique de chaque instance dans cette propriété. Dans l'événement OnClose de la fiche, on ajoutera Action =
caFree; pour détruire la fiche. (Une fiche enfant MDI est minimisée par défaut sur l'événement OnClose.)
#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *
Form2;
int
TForm2::Numero =
0
;
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2
(
TComponent*
Owner)
:
TForm
(
Owner)
{
Numero++
;
}
//---------------------------------------------------------------------------
void
__fastcall TForm2::FormClose
(
TObject *
Sender, TCloseAction &
Action)
{
Action =
caFree;
}
//---------------------------------------------------------------------------
void
__fastcall TForm2::FormCreate
(
TObject *
Sender)
{
Caption =
"
Fenêtre N°
"
+
IntToStr
(
Numero);
}
III. Construction de la fiche parent MDI▲
Nous allons d'abord donner les propriétés suivantes à sa Form (Form1) :
FormStyle à fsMDIForm ;
Caption à Application MDI.
Puis poser sur la fiche un MainMenu (MainMenu1) avec un premier menu nommé « Fichier » ayant deux options de menu, « New » et « Quitter » puis un second menu nommé « Fenêtre » avec trois options « Cascade », « Horizontale » et « Verticale ».
Unit1.cpp : tout d'abord inclure Unit2.h pour avoir accès au model de la fiche enfant. Puis sur l'événement OnClick de l'option de menu « New », nous créons dynamiquement une fiche enfant (Form2). Sur l'événement OnClick de « Quitter », nous fermons l'application. Sur l'événement OnClick de « Cascade », nous mettons les fenêtres enfants en cascade avec la méthode Cascade. Sur les événements OnClick de « Horizontale » et « Verticale », nous mettons les fenêtres enfants en mosaïque horizontale ou verticale à l'aide de la méthode Tile en fonction de l'état de la propriété TileMode :
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *
Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1
(
TComponent*
Owner)
:
TForm
(
Owner)
{
}
//---------------------------------------------------------------------------
void
__fastcall TForm1::New1Click
(
TObject *
Sender)
{
Form2 =
new TForm2
(
this);
}
//---------------------------------------------------------------------------
void
__fastcall TForm1::Quitter1Click
(
TObject *
Sender)
{
Close
(
);
}
//---------------------------------------------------------------------------
void
__fastcall TForm1::Cascade1Click
(
TObject *
Sender)
{
Cascade
(
);
}
//---------------------------------------------------------------------------
void
__fastcall TForm1::Horizontale1Click
(
TObject *
Sender)
{
TileMode =
tbHorizontal;
Tile
(
);
}
//---------------------------------------------------------------------------
void
__fastcall TForm1::Verticale1Click
(
TObject *
Sender)
{
TileMode =
tbVertical;
Tile
(
);
}
Vous pouvez dès maintenant compiler l'application. Vous pouvez vous apercevoir qu'avec peu de code elle est déjà fonctionnelle.
Vous pouvez voir que les fiches enfants occupent toute la zone client de la fenêtre parent. On peut se réserver une partie de la fiche parent pour mettre par exemple des boutons comme sur la capture d'écran que vous avez pu voir au début de cet article. Pour cela nous poserons sur la fiche un TPanel avec les propriétés suivantes :
Align à Left
Caption à Vide
BevelOuter à bvNone
Sur ce panel vous pouvez poser ce dont vous avez besoin. Dans la capture d'écran en exemple j'avais posé deux SpeedButtons dont j'avais affecté les événements OnClick aux méthodes des menus New1Click et Quitter1Click.
IV. Questions diverses▲
IV-A. Les menus▲
Si vos fenêtres enfants possèdent un MainMenu, il prendra la place du MainMenu de la fenêtre parent dès que l'une d'elles est ouverte. Si vous voulez fusionner les menus, il faudra utiliser leurs propriétés GroupeIndex afin de les organiser.
IV-B. Accès à la fiche enfant active depuis la fiche principale▲
Pour accéder à la fiche enfant qui détient le focus depuis la fiche principale, on utilise sa propriété ActiveMDIChild. Par exemple, nous allons ajouter dans notre menu « Fichier » l'option « Fermer » qui devra fermer la fiche active. Donc sur l'événement OnClick de « Fermer » rajouter ceci :
//...
void
__fastcall TForm1::Fermer1Click
(
TObject *
Sender)
{
if
(
ActiveMDIChild) ActiveMDIChild->
Close
(
);
}
//...
Vous remarquerez qu'avant de fermer une fiche enfant, nous testons qu'il en existe bien une. (ActiveMDIChild a la valeur NULL si aucune fiche enfant n'est présente.)
IV-C. Accès à un objet de la fiche enfant active depuis la fiche principale▲
Par exemple effacer le contenu de Memo1 de la fiche enfant active depuis la fiche principale. Créer une nouvelle option de menu avec son Caption à Clear, sur son événement OnClick mettre :
//...
void
__fastcall TForm1::Clear1Click
(
TObject *
Sender)
{
if
(
ActiveMDIChild)
((
TForm2 *
)ActiveMDIChild)->
Memo1->
Lines->
Clear
(
);
}
//...
Dans ce cas on est obligé d'effectuer un changement de type en pointeur sur TForm2, car le type de ActiveMDIChild est un pointeur sur TForm.
IV-D. Accès à toutes les fiches enfants▲
La fiche parent peut aussi accéder à ses fiches enfants par un indice à l'aide de la propriété MDIChildren. Exemple pour fermer toutes les fiches enfants, vous créez une nouvelle option dans le menu « Fichier » : « Tout fermer » sur son événement OnClick mettre :
//...
void
__fastcall TForm1::Toutfermer1Click
(
TObject *
Sender)
{
for
(
int
x =
MDIChildCount-
1
; x>=
0
; x--
)
MDIChildren[x]->
Close
(
);
}
//...
Attention toutefois à cet indice : il change en fonction de l'ordre auquel nous avons accédé aux fiches précédemment, la dernière fiche accédée a l'indice 0, donc évitez d'utiliser cet indice pour accéder à une fiche particulière.
IV-E. Passer le focus à la fiche suivante ou précédente▲
Pour cela on utilise les méthodes de la fiche parent Next et Previous. Vous pouvez remarquer que le changement de focus se fait encore en fonction de l'indice.
Vous avez maintenant quelques bases pour construire votre première application MDI.
CGi
Avec la contribution d'Alacazam pour la relecture.