IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Construction d'une application MDI

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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 :

Image non disponible

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 » :

 
Sélectionnez
#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.)

 
Sélectionnez
#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 :

 
Sélectionnez
#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 :

 
Sélectionnez
//...
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 :

 
Sélectionnez
//...
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 :

 
Sélectionnez
//...
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.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Copyright 2002-2016 CGi - Tous droits réservés CGi. Toutes reproduction, utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour un usage personnel doit faire l'objet d'une autorisation écrite de la part de l'auteur, propriétaire des droits intellectuels.
Les codes sources de ce document sont fournis en l'état. L'utilisateur les utilise à ses risques et périls, sans garantie d'aucune sorte de la part de l'auteur. L'auteur n'est responsable d'aucun dommage subi par l'utilisateur pouvant résulter de l'utilisation ou de la distribution des codes sources de ce document.
De la même façon, l'auteur n'est en aucun cas responsable d'une quelconque perte de revenus ou de profits, ou de données, ou de tous dommages directs ou indirects, susceptibles de survenir du fait de l'utilisation des codes sources de ce document, quand bien même l'auteur aurait été averti de la possibilité de tels dommages. L'utilisation des codes sources de ce document vaut acceptation par l'utilisateur des termes de la licence ci-dessus.