koossery.MVCwin : un framework MVC simple pour adresser les applications .NET Winforms

| M. YACOUBOU (Centre de Développements Projets forfaits .NET, KOOSSERY TECHNOLOGY) | B.Jc. BAKENEGHE  (Software Coordinator, KOOSSERY TECHNOLOGY) |

Cet article traite du développement de winforms .NET en mode MVC.

Posons un constat simple : si le pattern MVC est si intéressant, alors pourquoi le limiter au seul domaine des applications web avec asp.net.mvc ?
Comment le mettre en œuvre dans un projet winform ?

Le framework koossery.MVCwin est un framework open source qui permet de développer les applications winform en mode MVC. C'est un framework hébergé sur la forge Codeplex à l'adresse : http://koosserymvcwin.codeplex.com

Nous présentons dans cet article les guidelines de ce framework, ainsi qu'un tutoriel simple montrant son utilisation.

Vos réactions par l'intermédiaire de ce fil : Commentez

- Titre : koossery.MVCwin : un framework MVC simple pour adresser les applications .NET Winforms

- Auteurs : M. YACOUBOU (Centre de Développements Projets forfaits .NET, KOOSSERY TECHNOLOGY), B.Jc. BAKENEGHE  (Software Coordinator, KOOSSERY TECHNOLOGY)

- Parution : 30 juin 2009

- Mise à jour : 13 juin 2020

- Licence :

Sommaire

- I. Présentation du framework koossery.MVCwin

- I-A. Principes de base du framework koossery.MVCwin

- I-B. Principaux éléments du framework

- I-B-1. Les contrôleurs

- I-B-2. Les actions

- I-B-3. Le type de retour des actions

- I-B-4. Le ControllerManager

- I-B-5. Les vues

- II. Tutoriel : une application Winforms simple à base du framework mvc koossery.MVCwin

- II-A. Analyse du tutoriel

- II-A-1. Les data

- II-A-1-a. LoginViewData

- II-A-1-b. WelcomeViewData

- II-A-2. Les vues

- II-A-2-a. LoginView

- II-A-2-b. WelcomeView

- II-A-3. Les contrôleurs

- II-A-3-a. LoginController

- II-A-3-b. WelcomeController

- II-B. Structure du projet VisualStudio du tutoriel

- II-C. Exemple d'exécution

- III. Conclusion et liens

I - Présentation du framework koossery.MVCwin

I-A - Principes de base du framework koossery.MVCwin

Nous allons illustrer les principes de base du framework koossery.MVCwin en prenant l'exemple d'un utilisateur qui se logue dans une application Winforms :

Figure 1 : Schéma d'éxecution framework koossery.MVCwin, Use Case 'se loguer'

Dans le schéma ci-dessus, on distingue :

1- Le user après avoir saisi son login et son mot de passe sur la Winforms LoginView, clique sur le bouton 'entrer' de cette Winforms ;

2- Les données saisies sur la Winforms LoginView sont sauvegardées via DataBiding dans LoginViewData ;

3- Transfert du flow d'exécution au ControllerManager ;

4- Le ControllerManager examine la commande puis va dans ses fichiers de configuration afin de déterminer le contrôleur approprié ainsi que l'action du dit contrôleur: il transfère alors l'exécution à LoginController, action 'Login' ;

5- L'action 'Login' de LoginController va récupérer les données de la LoginViewData, fait les transformations nécessaires si besoin et ensuite invoque un service de la couche métier (back-end) pour les traitements d'authentification ;

6- Le service métier effectue des opérations et retourne un résultat à l'action 'Login' ;

7- L'action 'Login' prépare à nouveau les données à partir du résultat du service métier et par un retour de méthode elle transfert le flow d'exécution au ControllerManager. Le ControllerManager après analyse détermine la prochaine vue à afficher ou le prochain couple (controller, action) à invoquer.

I-B - Principaux éléments du framework

Les éléments fondamentaux de l'open source MVC winform koossery.MVCwin sont :

- les contrôleurs ;

- les actions des contrôleurs ;

- le type de retour des actions (interface IactionResult) ;

- le ControllerManager ;

- les vues.

I-B-1 - Les contrôleurs

Suite à un clic sur un bouton au niveau de la winform, le contrôleur approprié est invoqué. Ce contrôleur va être en charge de récupérer les données saisies, d'invoquer le back-end et ensuite de retourner la main au ControllerManager.
Tout contrôleur implémente l'interface IController.

I-B-2 - Les actions

Les actions sont les méthodes exposées par les contrôleurs.
Ce sont des méthodes publiques, non statiques, et elles ne doivent pas être surchargées. Une action peut par exemple invoquer les services du back-end.

I-B-3 - Le type de retour des actions

C'est le type de l'objet retourné par une action. Il implémente l'interface IActionResult. Ce peut être une vue à afficher (type spécialisé : ViewResult) ou une redirection vers une autre action (type spécialisé: RedirectToActionResult).

I-B-4 - Le ControllerManager

Représente l'entité en charge de manager les contrôleurs.

I-B-5 - Les vues

L'interface IView est le type implémenté par toutes les vues.
Le framework fournit une implémentation de base par défaut qui comporte un ensemble de fonctionnalités techniques.

II - Tutoriel : une application Winforms simple à base du framework mvc koossery.MVCwin

Dans cette partie nous allons effectuer un tutoriel simple afin d'illustrer de façon progressive et pas difficile l'utilisation du framework koossery.MVCwin dans un projet front-end Winforms. Bien entendu nous poursuivrons lors de publications ultérieures avec des tutoriels progressivement complets montrant l'utilisation des fonctionnalités avancées du framework koossery.MVCwin.

L'exemple simple que nous allons voir ci-dessous est une application Winforms avec trois vues dont le story-board est le suivant :

-  l'utilisateur lance l'application ;

- l'application affiche la Winforms d'authentification: LoginView ;

- l'utilisateur se logue avec succès (bouchon) et cela résulte en l'affichage de la vue d'accueil: WelcomeView ;

- sur la WelcomeView, l'utilisateur a différents menus: il choisit par exemple de créer un article en sélectionnant le menu de lancement de la vue de création d'un article; il fait ses saisies et ensuite valide la création de l'article.

II-A - Analyse du tutoriel

II-A-1 - Les data

On va avoir les datas suivants :

- loginViewData : données d'affichage de la LoginView ;

- WelcomeViewData : données d'affichage de la WelcomeView.

Toutes les Data héritent de la classe de base AbstractBaseData fournie par le Framework. AbstractBaseData implémente les interfaces INotifyPropertyChanged et IDataErrorInfo.

II-A-1-a - LoginViewData

LoginViewData contient les propriétés suivantes :

- String login ;

- String password.

Ci-dessous un extrait du code de LoginViewData :

Figure 2 : Extrait du Code de la classe LoginViewData

using System;
using System.Collections.Generic;
using System.Text;

using Koossery.MVCwin.Data;

namespace Koossery.MVCwin.Tuto.App_Code.data.session
{
public class LoginViewData : AbstractBaseData
{
string login;
string password;

public LoginViewData()
{
ResetData();
}

public string Login
{
get { return login; }
set
{
login = value;
PropertyHasChanged("Login");
}
}

public string Password
{
get { return password; }
set
{
password = value;
PropertyHasChanged("Password");
}
}

/// <summary>
/// Reseting the Data
/// </summary>
public void ResetData()
{
Login = null;
Password = null;
}
}
}

Dans l'extrait de code ci-dessus de LoginViewData, on note la présence de la méthode PropertyHasChanged(String propertyName) dans le setter des différentes propriétés. Cela a pour but premier le support bidirectionnel du DataBinding.

Les règles de validation n'étant pas embarquées dans la classe de base AbstractBaseData, le processus de validation pourra être fait en s'appuyant par exemple sur Microsoft Validation Application Block. Du fait de que AbstractBaseData implémente IDataErrorInfo, toute erreur renvoyée lors de la validation va être sauvegardée au sein de la data et sera par la suite affichée au niveau la winform.

II-A-1-b - WelcomeViewData

WelcomeViewData contient la propriété :

- String greetingMessage.

Ce message sera affiché lorsqu'un utilisateur se connecte.

Ci-dessous un extrait du code de WelcomeViewData :

Figure 3 : Extrait du code de la WelcomeViewData

using System;
using System.Collections.Generic;
using System.Text;
using Koossery.MVCwin.Tuto.Properties;
using Koossery.MVCwin.Data;

namespace Koossery.MVCwin.Tuto.App_Code.data.session
{
public class WelcomeViewData : AbstractBaseData
{
string greetingMessage = Resources.Welcome;

public string GreetingMessage
{
get { return greetingMessage; }
set { greetingMessage = value; }
}
}
}

II-A-2 - Les vues

Le tutoriel comporte deux vues qui sont : LoginView (pour se loguer) et WelcomeView (vue d'accueil).

II-A-2-a - LoginView

C'est la vue utilisée pour se connecter. L'utilisateur saisit son compte et mot de passe et appuie sur le bouton d'authentification. Les informations saisies par l'utilisateur sont automatiquement sauvegardées dans LoginViewData via le DataBinding.
Cette vue se présente comme suit :

Figure 4 : Login View

Toutes les vues du tutoriel héritent de la vue de base BaseView : c'est l'implémentation de base de l'interface IView fournie par le Framework.

Ci-dessous un extrait du code-behind de LoginView :

1 using Koossery.MVCwin.ControllerManager.impl;
2
3 namespace Koossery.MVCwin.Tuto.views.form
4 {
5 public partial class LoginView : BaseView
6 {
7     public LoginView()
8     {
9         InitializeComponent();
10     }
11
12     void BgAsyncMethods_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
13     {
14         //Cacher progressBar
15         plLogin.Visible = false;
16     }
17
18     void BgAsyncMethods_ProgressChanged(object sender,
ProgressChangedEventArgs e)
19     {
20         if (!plLogin.Visible) plLogin.Visible = true;
21         plLogin.Refresh();
22     }
23
24     private void btLogin_Click(object sender, EventArgs e)
25     {
26         string typeOfLoginViewData = typeof(LoginViewData).Name;
27         LoginViewData loginViewData =
28            this.GetSessionData(typeOfLoginViewData) as LoginViewData;
29
30         //Validating the data
31         ValidationResults results =
Utility.ValidateData<LoginViewData>(loginViewData);
32
33         epLoginView.DataSource = bsLoginViewData;
34
35         //Récupération de la VSD
36         if (!results.IsValid)
37         {
38             epLoginView.DataSource = bsLoginViewData;
39             return;
40         }
41
42         //Transfert de l'exécution vers le contrôleur
43         this.InvokeController(typeof(LoginController).Name,
44                                       ApplicationData.LoginAction_Login);
45     }
46
47     public override void BindDataToView()
48     {
49         LoginViewData loginViewData =
50               this.GetSessionData(typeof(LoginViewData).Name) as LoginViewData;

51         bsLoginViewData.DataSource = loginViewData;
52
53         //Displaying error message
54         DisplayMessages();
55
56         //Getting the controller manager
57         Koossery.MVCwin.ControllerManager.impl.ControllerManager
controllerManager =
58                     this.ControllerManager
as Koossery.MVCwin.ControllerManager.impl.ControllerManager;
59
60         //Subscription to the BackgroundWorker
61         controllerManager.BgAsyncMethods.ProgressChanged +=
62          new ProgressChangedEventHandler(BgAsyncMethods_ProgressChanged);
63
64         controllerManager.BgAsyncMethods.RunWorkerCompleted +=
65          new RunWorkerCompletedEventHandler(BgAsyncMethods_RunWorkerCompleted);
66
67     }
68
69     private void DisplayMessages()
70     {
71         lblMessageVisibility.Visible = this.GetContextVisibility();
72         //On récupère le message d'erreur
73         if (GetMessage(MessageType.ERROR_MESSAGE) != null)
74         {
75             lblMessageVisibility.ForeColor = Color.IndianRed;
76             lblMessageVisibility.Text = GetMessage(MessageType.ERROR_MESSAGE);
77         }
78         else if (GetMessage(MessageType.OTHER_MESSAGE) != null)
79         {
80             lblMessageVisibility.ForeColor = Color.Green;
81             lblMessageVisibility.Text = GetMessage(MessageType.OTHER_MESSAGE);
82         }
83     }
84
85     private void lbExit_Click(object sender, EventArgs e)
86     {
87         Application.Exit();
88     }
89
90
91     }
}

- La ligne 31 : montre la validation des données de la LoginViewData. Cette validation est faite via la méthode ValidateData() se trouvant dans la classe Utility. La validation est prise en charge par le framework tiers Microsoft Validation Application Block. La validité d'une donnée est vérifiée via la propriété IsValid de l'objet ValidationResults renvoyé par la méthode de validation. Si la donnée n'est pas valide alors la source du contrôle non visible ErrorProvider est fixée afin de fournir un bilan visuel de l'erreur rencontrée à l'utilisateur.

- La ligne 54 : montre l'invocation de l'action 'Login' du contrôleur LoginController. Une demande explicite est adressée au Manager pour l'exécution d'une action précise d'un contrôleur précis. La demande explicite est traduite par l'appel de la méthode InvokeController(String controllerName, String actionName).

- La ligne 47 : montre la redéfinition de la fonction BindDataToView qui permet de faire un binding entre les Data et les différents contrôles sur la vue d'une part et d'autre part la souscription aux évènements générés par le background worker interne.

II-A-2-b - WelcomeView

Elle représente la vue d'accueil et contient un menu offrant certaines fonctions.

Figure 5 : Vue d'accueil

Elle étend BaseView. Un extrait de son code-behind est le suivant :

Extrait du Code-behind de la WelcomeView

using Koossery.MVCwin.ControllerManager.impl;

namespace Koossery.MVCwin.Tuto.views.form
{
public partial class WelcomeView : BaseView
{
public WelcomeView()
{
InitializeComponent();
}

public override void BindDataToView()
{
WelcomeViewData welcomeViewData =
this.GetSessionData(typeof(WelcomeViewData).Name) as
WelcomeViewData;

tsWelcome.Text = welcomeViewData.GreetingMessage;
bsWelcomeViewData.DataSource = welcomeViewData;

//Ajout des évènements
Koossery.MVCwin.ControllerManager.impl.ControllerManager
controllerManager =
this.ControllerManager as
Koossery.MVCwin.ControllerManager.impl.ControllerManager;

//Souscription aux évènements
controllerManager.BgAsyncMethods.ProgressChanged +=
new ProgressChangedEventHandler(BgAsyncMethods_ProgressChanged);

controllerManager.BgAsyncMethods.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(BgAsyncMethods_RunWorkerCompleted);
}

private void seDéconnecterToolStripMenuItem_Click(object sender,
EventArgs e)
{
LoginViewData login =
this.GetSessionData(typeof(LoginViewData).Name) as
LoginViewData;

login.ResetData();

this.InvokeController(typeof(LoginController).Name,
ApplicationData.InitAction);
this.Hide();
}

private void quitterToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}

II-A-3 - Les contrôleurs

Les contrôleurs de notre exemple sont : LoginController et WelcomeController.

II-A-3-a - LoginController

LoginController contient les actions Init et Login.

- L'action 'Init' est chargée d'afficher la LoginView.

- L'action 'Login' est chargée d'authentifier l'utilisateur.

LoginController étend la classe d'implémentation de base de l'interface IController, à savoir ControllerBase. Bien entendu, le Framework étant ouvert, on peut choisir de ne pas étendre la classe de base ControllerBase et donc d'implémenter directement IController.

Examinons à présent un extrait de code de la l'action Init().

Figure 7: Extrait du Code de l'action Init de LoginController

1 public IActionResult Init()
2         {
3             //Clear all messages
4             this.ClearErrorsAndMessage();
5
6             //Display the loginView
7             return this.View(typeof(LoginView).Name);
}

- La ligne 4 : Cette méthode est appelée pour vider les messages d'erreur. C'est une fonctionnalité qui provient de l'implémentation de base ControllerBase

- La ligne 7 : Demande l'affichage de la LoginView au manager.

Examinons à présent un extrait de code de l'action 'Login':

Figure 8: Extrait du Code de l'action Login de LoginController

1 [Asynchronous]
2 public IActionResult Login()
3 {
4     //Getting the login view sessionData
5     LoginViewData loginViewData =
6        this.GetSessionData(typeof(LoginViewData).Name) as LoginViewData;
7     try
8     {
9         //We are doing that just for BackgroundWorker testing purpose.
10         //We are sure that in your reel project you won't do that
11         System.Threading.Thread.Sleep(1000);
12
13         //Clear error messages
14         this.ClearErrorsAndMessage();
15
16         //Authenticating user
17         if (loginViewData.Login == "admin" && loginViewData.Password == "admin")
18         {
19             //We are doing that just for BackgroundWorker testing purpose.
20             //We are sure that in your reel project you won't do that
21              System.Threading.Thread.Sleep(1000);
22
23              return this.RedirectToAction(typeof(WelcomeController).Name,
24                                                  ApplicationData.InitAction);
25          }
26
27          //The user does not exists
28          strMsg = String.Format(
XmlUtility.getKeyValueFromXmlConfigFile(
29                  errorMessageXmlFileName, "LOGIN", "LOGIN-002"),
new object[1]);
30
31         this.AddMessage(strMsg, MessageType.ERROR_MESSAGE);
32
33         //We are doing that just for BackgroundWorker testing purpose.
34         //We are sure that in your reel project you won't do that
35         System.Threading.Thread.Sleep(1000);
36         return this.View(typeof(LoginView).Name);
37     }
38     catch (Exception ex)
39     {
41         arg = new Object[3];
42         arg[0] = loginViewData.Login;
43         arg[1] = FrontEndConstants.TECHNICAL_DETAIL;
44         arg[2] = ex.Message;
45         strMsg = String.Format(XmlUtility.getKeyValueFromXmlConfigFile(
46                            errorMessageXmlFileName, "LOGIN", "LOGIN-001"),arg);
47         log.Error(strMsg);
48         String strReturnMessage = strMsg.Substring(0,
49                            strMsg.IndexOf(FrontEndConstants.TECHNICAL_DETAIL));
50
51         this.AddMessage(strReturnMessage, MessageType.ERROR_MESSAGE);
52
53         return this.View(typeof(LoginView).Name);
}
}

- La ligne 1 : permet de configurer l'action Login en mode d'exécution asynchrone. Cette action sera par conséquent exécutée dans un thread séparé.

- La ligne 5 : on récupère une instance de la classe LoginViewData associée à la session user.

- La ligne 23 : redirection de l'exécution vers le contrôleur et l'action passés en paramètre dans le cas où l'authentification a réussi.

- La ligne 28 : montre comment les messages d'erreurs sont récupérés du fichier xml via la classe utilitaire XmlUtility fournie par le framework. Les clés sont passées en paramètre à la méthode getKeyValueFromXmlConfigFile.

- La ligne 31: montre comment le message est ajouté comme message d'erreur en utilisant l'énumération MessageType.ERROR_MESSAGE. Cela aura pour effet l'affichage du message d'erreur sur la vue.

II-A-3-b - WelcomeController

Dans le cas présent ce contrôleur ne contient qu'une seule action : Init.

Figure 9: Extrait du code de l'action Init du contrôleur WelcomeController

public class WelcomeController : ControllerBase
{
public IActionResult Init()
{
//Getting de view data
LoginViewData loginViewData =
this.GetSessionData(typeof(LoginViewData).Name) as LoginViewData;

WelcomeViewData welcomeViewData =
this.GetSessionData(typeof(WelcomeViewData).Name) as WelcomeViewData;

welcomeViewData.GreetingMessage =
"Welcome to the Koossery.MVCwin Tutorial";

//Initializing datas
welcomeViewData.GreetingMessage += loginViewData.Login;

return this.View(typeof(WelcomeView).Name);
}
}

II-B - Structure du projet VisualStudio du tutoriel

La structure du projet est la suivante :

Structure du projet Tutorial

On distingue :

- le répertoire regroupant les contrôleurs ;

- le répertoire regroupant les data ;

- le répertoire regroupant les configurations.

Ci-dessous un extrait du fichier spring de configuration des contrôleurs :

Figure 11: Extrait du fichier spring de configuration des contrôleurs

<!--Configuration des Controllers-->
<object id="WelcomeController"
type="Koossery.MVCwin.Tuto.App_Code.controllers.WelcomeController">
<property name="Session">
<ref object="Session"/>
</property>
</object>

<object id="AboutController"
type="Koossery.MVCwin.Tuto.App_Code.controllers.about.AboutController">
<property name="Session">
<ref object="Session"/>
</property>
</object>

<!--configuration du LoginController-->
<object id="LoginController"
type="Koossery.MVCwin.Tuto.App_Code.controllers.LoginController">
<property name="Session">
<ref object="Session"/>
</property>
<property name="AsynchMethods">
<list element-type="System.String">
<value>Login</value>
</list>
</property>
</object>

<!--Controleur-->
<object id="ControleurManager"
type="Koossery.MVCwin.ControllerManager.impl.ControllerManager,
Koossery.MVCwin">
<property name="DefaultController">
<value>LoginController</value>
</property>

<!--les controleurs-->
<property name="Controllers">
<dictionary key-type="string"
value-type="Koossery.MVCwin.Controller.itf.IController, Koossery.MVCwin">
<entry key="LoginController">
<ref object="LoginController"/>
</entry>
<entry key="WelcomeController">
<ref object="WelcomeController"/>
</entry>
<entry key="AboutController">
<ref object="AboutController"/>
</entry>
</dictionary>
</property>

<!--Etats-->
<property name="Views">
<dictionary key-type="string"
value-type="Koossery.MVCwin.Views.itf.IView, Koossery.MVCwin">
<entry key="LoginView">
<ref object="LoginView"/>
</entry>

II-C - Exemple d'exécution

Nous avons essayé l'authentification avec un User Name et un Password incorrects, d'où le message en rouge au-dessus des contrôles. Comme vous l'avez deviné, ce message provient de notre fichier xml de configuration des messages d'erreurs. En saisissant le bon User Name et Password (ie UserName = admin, pwd = admin) on obtient la WelcomeView.

III - Conclusion et liens

Le framework koossery.MVCWin permet au Centre de Développement des Projets forfaits .NET de Koossery Technology de pouvoir développer des applications .NET Winforms de taille conséquente et évolutive en mode MVC.

Cela a pour effet de fortement spécialiser les intervenants :

- spécialistes design des Winforms ;

- développeurs des contrôleurs et des actions ;

- coordination des configurations.

Vous pouvez télécharger le zip du tutoriel ici: zip du tutoriel

koossery.MVCwin est un framework open source dont le lien sur Codeplex est : http://koosserymvcwin.codeplex.com.