I. Énoncé de l'exemple▲
Pour démontrer la POO appliquée aux formulaires, nous allons réaliser une petite application simpliste gérant une liste de formations à la carte dispensées par une école. Imaginons qu'il y ait deux types de formations :
- les formations dont le coût de l'inscription est fixe ;
- les formations dont le coût de l'inscription est variable en fonction du nombre de participants.
L'exercice consiste juste à fournir à l'utilisateur une interface grâce à laquelle il pourra encoder par exemple ceci :
Intitulé de formation |
Nombre d'heures |
Identifiant professeur |
---|---|---|
Oracle |
125 |
130 |
Dotnet |
128 |
150 |
Une seule variante sera gérée à l'encodage, soit le coût de la formation sera fourni de manière forfaitaire par l'utilisateur, soit ce coût variera en fonction du nombre de participants. Dans ce cas, l'utilisateur fournira à la fois le coût par participant et le nombre de participants. L'application gardera en mémoire les instances de formations encodées par l'utilisateur. Nous ne stockerons rien en base de données.
I-A. Réalisation de notre composant.▲
Nous savons que nous devons gérer des formations. La première chose que nous faisons est de chercher les points communs de toutes les formations. Parmi ceux-ci, nous pouvons établir la liste (non exhaustive) suivante :
- le libellé (toutes les formations disposent d'un libellé) Dotnet, Oracle… ;
- l'identifiant du formateur (tous les cours sont dispensés par un formateur) ;
- le nombre d'heures.
Nous pourrions bien sûr trouver plus de points communs et étoffer notre composant pour gérer tous les cas de figure, mais ce n'est pas le but de cet article. Concentrons-nous sur les mécanismes POO impliqués.
Identifions à présent, tout en restant fidèle à notre énoncé, les points qui diffèrent parmi les formations que nous désirons gérer :
- le coût de la formation qui peut être soit forfaitaire, soit dépendant du nombre de participants.
Nous pouvons dès lors conclure que nous avons à gérer de manière spécifique la manière dont le coût de la formation sera calculé. Pour certaines formations, le nombre de participants ainsi que le coût par participant devront être connus, alors que pour d'autres, le prix forfaitaire suffira. Sachant cela, nous pouvons désormais élaborer notre composant de la manière suivante…
I-B. Vue logique de notre composant▲
Notre composant est composé de quatre classes.
- La classe « formation » qui est une super classe abstraite contenant les propriétés communes et obligeant les classes dérivées à implémenter la méthode « cout_formation ». Cette méthode oblige les classes dérivées à implémenter leur propre mécanisme de calcul de coût.
- La classe « formation_cout_fixe » est une classe dérivée de « formation ». Elle implémente son propre mécanisme de coût de formation consistant à simplement recevoir le coût forfaitaire en paramètre.
- La classe « formation_cout_variable » est une classe dérivée de « formation ». Elle implémente son propre mécanisme de coût de formation consistant à multiplier le nombre de participants par le coût par participant.
- La classe « gestion_formation » qui permet de stocker des instances de « formation_cout_fixe » et « formation_cout_variable » dans une ArrayList. C'est cette instance qui sera le lien entre le composant et l'interface qui va l'utiliser.
Vous pourriez bien sûr créer d'autres classes dérivées qui répondraient à d'autres besoins en matière de coût de formation. Je le répète, le but de cet article n'est pas de créer un composant gérant les formations, mais bien de comprendre la logique objet que l'on peut appliquer au niveau des formulaires windows.
Notre DLL étant créée, on peut désormais l'utiliser avec tout type d'interface (web, windows forms, console…). Nous allons à présent voir comment il est possible d'appliquer la même logique au niveau d'une interface windows forms.
II. Scénario d'implémentation de nos windows forms▲
Dans cette section, je vais décrire les étapes principales que j'ai suivies pour créer mon application. Vous pourrez visualiser le détail en téléchargeant l'exemple.
Les étapes
- Création d'un projet « Windows Form en C# ».
- Ajout de la référence vers notre composant.
- Le formulaire principal créé par défaut par le projet doit être transformé en formulaire MDI. Pour ce faire, il faut modifier la propriété « IsMdiContainer » et la mettre à « true ».
- Ajouter les menus « Fichier -> quitter l'application » et "Formations -> Formation forfaitaire, Formation Variable, Liste. Ces menus servent à ouvrir les fenêtres pour l'encodage des formations et pour les lister.
Maintenant que notre formulaire MDI et nos menus sont prêts, nous allons créer nos autres formulaires en suivant la logique de notre composant. Nous allons d'abord commencer par ajouter le formulaire qui correspond à la classe « formation » de notre composant. Il faut donc ajouter un formulaire à notre projet.
Lorsque ce formulaire est ajouté, nous devons créer les zones de texte communes, à savoir :
- l'intitulé de la formation ;
- l'identifiant du professeur ;
- le nombre d'heures.
Il faut donc ajouter trois contrôles de type « label » et trois contrôles de type « textbox ». Ne perdons pas de vue le fait que ces contrôles représentent les caractéristiques communes de nos formations. Dès lors, nous allons modifier leur portée et les basculer en « protected ».
Il faut modifier la propriété « Modifiers » de vos contrôles et la basculer en « Protected ». Par défaut, le « Modifier » est positionné sur « Private ». Le contrôle n'est donc disponible que pour le formulaire courant. Le fait d'avoir modifié cette propriété rend ce contrôle utilisable par une classe dérivée.
Comme vous vous en doutez sûrement, nous devons à présent rajouter deux autres formulaires :
- l'un correspondant à la classe « formation_cout_variable » de notre composant ;
- l'autre correspondant à la classe « formation_cout_fixe » de notre composant.
Pour ce faire, il est nécessaire d'ajouter tour à tour ces formulaires. Selon l'IDE que vous utilisez, il vous sera soit possible d'ajouter les formulaires en utilisant le template « Inherited windows form », soit vous devez ajouter un formulaire standard « Windows Form ». Si vous devez opter pour la deuxième solution, veillez à changer l'héritage par défaut qui est effectué lorsque vous créez un formulaire.
Vous devez en effet faire hériter vos deux nouveaux formulaires du formulaire « formation » que vous venez de créer. Par défaut, un formulaire hérite automatiquement de la classe « Form », remplacez « Form » par le nom de votre formulaire « formation ». Si tout se passe bien, lorsque vous basculerez en mode « design », vous devriez apercevoir les zones de textes crées au niveau du formulaire formation.
Voici un exemple de code mettant en exergue l'héritage du formulaire.
using
System;
using
System.
Collections.
Generic;
using
System.
ComponentModel;
using
System.
Data;
using
System.
Drawing;
using
System.
Text;
using
System.
Windows.
Forms;
using
Formations;
namespace
InterfaceFormations
{
// "InterfaceFormations.formation" a remplacé "Form". Cet héritage permet de bénéficier des zones de textes
// que l'on a basculé préalablement en mode "protected" au niveau de notre formulaire "formation"
public
partial
class
formation_variable :
InterfaceFormations.
formation
{
public
formation_variable
(
)
{
InitializeComponent
(
);
}
private
void
ajoutFormation_Click
(
object
sender,
EventArgs e)
{
try
{
gest.
ajout_formation
(
intitule.
Text,
int
.
Parse
(
id_prof.
Text),
int
.
Parse
(
nb_heures.
Text),
int
.
Parse
(
nb_part.
Text),
float
.
Parse
(
cout_part.
Text));
}
catch
(
gestion_formation_exception ajoutFrmEx)
{
MessageBox.
Show
(
ajoutFrmEx.
Message);
}
catch
(
Exception ex)
{
MessageBox.
Show
(
ex.
Message);
}
}
}
}
Note: le formulaire « formation » étant supposé jouer le rôle de la classe « formation » de notre DLL, il doit logiquement être déclaré comme « abstract ». Selon l'IDE que vous utilisez, vous pourriez rencontrer un problème lorsque vous basculerez en mode « design » l'un de vos formulaires dérivés. Ceci est clairement un « bug » de l'IDE. Il est donc vivement souhaitable de déclarer le formulaire en « abstract » à la fin du développement pour éviter ces désagréments. J'ai rencontré ce problème dans Visual C# Expression edition, mais aussi dans Visual Studio .NET 2003.
Nous avons à présent « recréé » la même logique de programmation orientée objet que celle que nous avions adoptée lors de la réalisation de notre DLL, car, si nous répertorions nos formulaires actuels et les liens qui les unissent, voici, schématiquement, ce que nous obtenons :
Les mécanismes d'héritage, de portée de membre au niveau des formulaires sont exactement les mêmes qu'au niveau d'une quelconque autre application. Ils sont très intéressants à connaître, car, comme nous l'avons vu, nous pouvons créer un « squelette » graphique de notre application contenant tous les champs communs à divers formulaires d'encodage et/ou d'affichage. Chaque formulaire d'encodage ou d'affichage pourra ensuite se spécialiser selon ses propres besoins.
Ceci nous permet d'avoir une approche graphique structurée.
II-A. Extraits de code importants▲
Dans cette section sont affichées certaines portions de code importantes par rapport au type d'exemple mis en œuvre.
Lorsque l'on charge un formulaire à partir du menu, dans un contexte MDI comme le nôtre, il faut charger la feuille comme suit :
formation_fixe f =
new
formation_fixe
(
);
//Dire quel est le conteneur MDI. This étant le formulaire MDI depuis lequel on charge la feuille.
f.
MdiParent =
this
;
f.
Show
(
);
Voici la classe formation de notre DLL.
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
namespace
Formations
{
public
abstract
class
formation
{
protected
string
intitule;
protected
int
identifiant_prof;
protected
int
heures;
//Classe déclarée abstraite
public
formation
(
string
arg_intitule,
int
arg_identifiant_prof,
int
arg_heures)
{
intitule=
arg_intitule;
identifiant_prof=
arg_identifiant_prof;
heures=
arg_heures;
}
public
string
get_fiche
(
)
{
return
intitule +
" "
+
identifiant_prof +
" "
+
heures;
}
//méthode que les classes dérivées doivent implémenter
public
abstract
float
cout_formation
(
);
}
}
Et voici la classe « formation_cout_variable », le lien entre « formation_cout_fixe » et « formation » étant le même, je ne l'inclurai pas ici.
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
namespace
Formations
{
public
class
formation_cout_variable :
formation //On hérite de "formation"
{
private
int
nb_participants =
0
;
private
float
cout_par_participant =
0
.
0F
;
public
formation_cout_variable
(
string
arg_intitule,
int
arg_identifiant_prof,
int
arg_heures,
int
arg_nb_part,
float
arg_cout_part)
//on appelle le constructeur de formation avec les éléments partagés.
:
base
(
arg_intitule,
arg_identifiant_prof,
arg_heures)
{
nb_participants =
arg_nb_part;
cout_par_participant =
arg_cout_part;
}
//On implémente "cout_formation"
public
override
float
cout_formation
(
)
{
return
nb_participants *
cout_par_participant;
}
}
}
Je ne parlerai pas dans ce tutoriel de la manière dont les informations sont mémorisées. Je vous laisse découvrir cela au niveau du code.
II-B. Conclusion▲
Nous avons vu qu'il est possible d'appliquer les mécanismes orientés objet au niveau de nos formulaires. Ceci apporte à mon sens un élément essentiel : la possibilité de créer un squelette graphique de son application et par la même, éviter la redondance de contrôles.