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).
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 quelque 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 pourait 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);
}
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 à alLeft
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é 2 SpeedButtons dont j'avais affecté les événements OnClick aux méthodes
des menus New1Click et Quitter1Click.
Questions diverses :
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'elle est ouverte.
Si vous voulez fusionner les menus, il faudra utiliser leurs propriétés GroupeIndex
afin de les organiser.
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.)
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.
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.
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 ce 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.
|