Bonjour,
environnement : visual studio 2010, C#

Lors de la reprise d'une petite application (3 couches) que j'avais faite j'ai décidé d'utiliser :
Le patern Model-View-Presenter pour la partie UI, avec le style Présentateur observation. (pour un couplage faible)

J'ai aussi choisit de gérer l'ouverture de mes fenêtres à travers une classe que j'ai nommé "chef d'orchestre".

J'aimerais avoir votre avis sur 2 choix que j'ai fait.
(pour situer en résumé : 1-l'ouverture des fenêtres centralisée; 2-une réponse du presenter au viewer face à un risque d'erreur à la fermeture)

Je prends comme exemple une fenêtre (config)

Dans ma fenêtre
je défini un presenter
je le crée au chargement de celle-ci
Puis lui envoi des évènements en fonction des actions utilisateurs.
J'ouvre d'autres fenêtres en envoyant des évènements au chef d'orchestre

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    /// <summary>classe de la fenêtre de configuration</summary>
    public partial class frmConfig : Form, IConfig
    {
        /// <summary>Définition du Presenter (gestionnaire) pour la fenêtre Config.</summary>
        private ClassGestConfig oGestConfig;
//...
        /// <summary>propriété Presenter (gestionnaire) de la fenêtre Config spécifique pour le chef d'orchestre.</summary>
        public ClassGestConfig GestConfig { get { return oGestConfig; } set { ; } }
        /// <summary>information des actions de l'utilisateur.</summary>
        public string sCfgInfo { set { this.sbConfig.Text = value; } }
//...
        /// <summary>évenement de demande au gestionnaire de la fenêtre Config d'effectuer les traitements de chargement.</summary>
        public event EventHandler LoadConfig;
        /// <summary>évenement de demande au gestionnaire de la fenêtre Config d'enregistrer la config.</summary>
        public event EventHandler EvtEnregistreConfig;
        /// <summary>évenement de demande au gestionnaire de la fenêtre Config d'afficher les mises à jours visuelles concernant la fenêtre de visu.</summary>
        public event EventHandler EvtMAJAfficherLogs;
        /// <summary>évenement de demande au chef d'orchestre d'ouvrir la fenêtre de visualisation pour afficher les logs.</summary>
        public event EventHandler EvtOuvreFenVisuAffLogs;
//...
        private void frmConfig_Load(object sender, EventArgs e)
        {
            oGestConfig = new ClassGestConfig(this, mCtx);
            LoadConfig(this, EventArgs.Empty);
        }
//--------------------------
//Avis N°1 - pour l'ouverture des fenêtres
//--------------------------
        private void btnCfgVoirLogs_Click(object sender, EventArgs e)
        {
            EvtMAJAfficherLogs(this, EventArgs.Empty);      // pour le presenter (gestionnaire de la vue)
            EvtOuvreFenVisuAffLogs(this, EventArgs.Empty);  // pour le chef d'orchestre (gestionnaire des fenêtres)
        }
//--------------------------
//Avis N°2 - enchaînement d'action -- en réponse allons nous utiliser un évènement ou un appel de méthode
//--------------------------
        private void btnCfgValider_Click(object sender, EventArgs e)
        {
            EvtEnregistreConfig(this, EventArgs.Empty);
            // et si cela ce passe mal ??? solutions 
            // 1- flag mis à jour par le presenter -> question : risque on de passe à la suite du code avant la fin de la sauvegarde ? (non retenue sol3 plus directe et plus sure)
            // 2- evt en retour pour autoriser la fermeture. -> sembler interressant en évènementiel (difficulté de mise en oeuvre) solution non résolue
            // 3- méthode fermer -> simplicité et efficacité (retenue) puisque le presenter est le gestionnaire des affichages de la fenêtre il peut la fermer après-tout.
            //Close();
        }
 
        /// <summary>fermeture de la fenêtre - disponible pour le présenter (gestionnaire de la fenêtre config).</summary>
        public void Fermer() { Close(); }
//...
}
L'interface correspondant (rien de spécial)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    /// <summary>Interface de la fenêtre de Config.</summary>
    public interface IConfig
    {
        /// <summary>information des actions de l'utilisateur.</summary>
        string sCfgInfo { set; }
//...
        /// <summary>évenement de demande au gestionnaire de la fenêtre Config d'effectuer les traitements de chargement.</summary>
        event EventHandler LoadConfig;
        /// <summary>évenement de demande au gestionnaire de la fenêtre Config d'enregistrer la config.</summary>
        event EventHandler EvtEnregistreConfig;
        /// <summary>évenement de demande au gestionnaire de la fenêtre Config d'afficher les mises à jours visuelles concernant la fenêtre de visu.</summary>
         event EventHandler EvtMAJAfficherLogs;
        /// <summary>évenement de demande au chef d'orchestre d'ouvrir la fenêtre de visualisation pour afficher les logs.</summary>
        event EventHandler EvtOuvreFenVisuAffLogs;
//...
        /// <summary>fermeture de la fenêtre de config - disponible pour le présenter (gestionnaire de la fenêtre config).</summary>
        void Fermer();
    }
Le presenter (gestionnaire de ma fenêtre)
Répond aux évènements de la fenêtre - met à jour les affichages
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
    /// <summary>Classe Presenter (gestionnaire) de la fenêtre Config.</summary>
    /// <remarks>traite toutes les informations d'affichage de la vue et les événements de celle-ci.</remarks>
    public class ClassGestConfig : ClassGestGen
    {
#region "champs Presenter"
        /// <summary>Définition du viewer (vue) pour la fenêtre Config.</summary>
        private IConfig VueConfig;          
#endregion
 
#region "constructeurs Presenter"
        /// <summary>constructeur simple</summary>
        /// <param>"oVueConfig" : l'instance de l'interface de liaison pour le viewer. (fenêtre Config)</param>
        public ClassGestConfig(IConfig oVueConfig)
            : base()
        {
            VueConfig = oVueConfig;         // reçoit le viewer et le concerve pour communiquer avec lui
            AbonnementEvt();                // abonnement aux évènements
        }
 
        /// <summary>constructeur avec contexte de travail.</summary>
        /// <param>"oVueConfig" : l'instance de l'interface de liaison pour le viewer. (fenêtre Config)</param>
        /// <param>"oCtx" : l'instance du contexte de travail</param>
        /// <remarks>constructeur a utiliser de préférence.</remarks>
        public ClassGestConfig(IConfig oVueConfig, ClassContexte oCtx)
            : base(oCtx)
        {
            VueConfig = oVueConfig;         // reçoit le viewer et le concerve pour communiquer avec lui
            AbonnementEvt();                // abonnement aux évènements
        }
#endregion
 
#region "méthode liées aux évènements Presenter"
        /// <summary>Abonnement aux évènements. ()</summary>
        private void AbonnementEvt()
        {
            // abonnement évènement unique - frmconfig ouvert en modal (showdialog)
            VueConfig.LoadConfig += OnLoadConfig;
            VueConfig.EvtEnregistreConfig += OnEnregistreConfig;
            VueConfig.EvtMAJAfficherLogs += OnAfficheLogs;
//...
        }
 
        /// <summary>réagit sur le chargement de la fenêtre afin d'entreprendre les actions correspondantes. ()</summary>
        private void OnLoadConfig(object sender, EventArgs e)
        {
            if (Ctx != null) { Initialise(); }
        }
 
        /// <summary>réagit à la demande d'enregistrer la config. ()</summary>
        private void OnEnregistreConfig(object sender, EventArgs e)
        {
//--------------------------
// Avis N°2 - choix de la demande de la fermeture de la fenêtre par le viewer à travers une méthode.
//--------------------------
		if (Ctx != null) { if (EnregistreConfig()==0) VueConfig.Fermer(); }
        }
 
        /// <summary>réagit à la demande d'afficher les logs. ()</summary>
        private void OnAfficheLogs(object sender, EventArgs e)
        {
            if (Ctx != null) { VueConfig.sCfgInfo = "Ouverture fenêtre de visualisation des logs. "; }
        }
//...
#region "méthode développeur"
        private int EnregistreConfig()
       { 
//...
       }
//...
}

le chef d'orchestre gestionnaire des fenêtres
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    /// <summary>Classe chef d'orchestre des fenêtres de l'application..</summary>
    class ClassChefOrchestre 
    {
#region "champs "
        private IDemarrage VueDemarrage;        // le viewer sur la fenêtre de démarrage.
//...
#region "méthode constructeurs"
        /// <summary>constructeur simple.</summary>
        /// <param>"oVueDemarrage" : l'instance de l'interface de liaison</param>
        public ClassChefOrchestre(IDemarrage oVueDemarrage)
        {
            VueDemarrage = oVueDemarrage;       // reçoit le viewer de la fenêtre principale  et le conserve pour communiquer avec lui
            VueDemarrage.OuvreFenConfig += OnOuvreFenConfig;
//...
        }
//...
#region "méthode liées aux évènements"
        /// <summary>réagit sur la demande d'ouverture de la fenêtre afin de la créer.</summary>
        private void OnOuvreFenConfig(object sender, EventArgs e)
        {
                frmConfig fenConfig = new frmConfig(mCtx);
                // controle un seul abonnement évènement - ouverture en modal
//--------------------------
// avis N°1 - c'est le chef d'orchestre qui gére la demande d'ouverture d'autres fenêtres
//--------------------------
                fenConfig.EvtOuvreFenVisuAffLogs += OnOuvreFenVisuAffLog;
                fenConfig.ShowDialog();
        }
 
        /// <summary>réagit sur la demande d'ouverture de la fenêtre de visu afin de la créer et d'afficher les logs.</summary>
        private void OnOuvreFenVisuAffLog(object sender, EventArgs e)
        {
                frmVisu fenVisualisation = new frmVisu();
                fenVisualisation.LargeurColonne(7, 500);
//init divers 
                fenVisualisation.ShowDialog();
        }
//...
}
J'ai essayer de simplifier le code pour cibler mes demandes mais aussi d'en laisser suffisement pour une compréhension globale.

Avis N°1 : j'effectue l'ouverture de mes fenêtres à travers le chef d'orchestre
Raison
1- c'est son rôle ( et c'est lui qui gère toutes les demandes d'ouverture) afin de centraliser ces opérations.
2- même si ma fenêtre de config doit en ouvrir une autre ce n'est pas le rôle du presenter de s'occuper des autres fenêtre. Le presenter gère les sollicitations de la fenêtre config (vers la couche métier) et met à jour les affichages.

Qu'elle est votre avis sur cette façon de procéder ?


Avis N°2 : lorsque l'utilisateur valide j'enregistre et ferme la fenêtre. ( je ne veux pas qu'il ai à faire 2 manip : valider + fermer)
Pendant l'enregistrement de la config j'ai un risque d'erreur (x ou y peu importe)
Vue que la demande s'effectue par un évènement je ne vais pas faire un close après l'envoie de l'évènement. (si erreur, adieu les info)
1- soit le presenter renvoie un évènement de fermeture vers la fenêtre si tout va bien. Nous avons une continuité dans la gestion évènementielle. (je n'ai pas réussi a mettre en pratique cette solution)
2- soit j'appelle directement une méthode de la fenêtre config depuis le presenter. (puisque c'est lui qui pilote la fenêtre) Cela a l'avantage d'être très simple. (c'est la solution que j'ai retenu)

Qu'en pensez-vous ?


Merci d'avoir suivi.
Vos remarques sont les bienvenues.

A+, Hervé.