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 :
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 :
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 :
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 :
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.
Elle étend BaseView. Un extrait de son code-behind est le suivant :
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().
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':
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.
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 :
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 :
<!--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 TechnologyKoossery 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 tutorielzip du tutorial
koossery.MVCwin est un framework open source dont le lien sur Codeplex est : http://koosserymvcwin.codeplex.comhttp://koosserymvcwin.codeplex.com.