Tawfik Nassar
10/2002
Qu'y a-t-il dans cet article?!
L'article explore le contexte de l'entrée en force de la plateforme .NET, les nouveautés et avantages qu'apporte cette nouvelle plateforme avec une comparaison entre un environnement de développement web 'traditionnel', ici ASP, et ASP.NET en tant que seule alternative cohérente et accessible actuellement disponible.
Il aborde ensuite un schéma réaliste de la migration vers .NET avec un exemple pratique de migration d'une section de code consacrée à la gestion d'un élément graphique (les onglets) dans une application web.
Il s'agit d'une section de code présentée dans un article sur l'automatisation de gestion éditoriale des sites web avec l'utilisation de ASP lié à une base de données.
Configuration nécessaire
Le code inclus dans l'article a été testé dans les configurations suivantes :
- Windows 2000 (Professional / Server / Advanced Server)
- Windows XP Professional
- IIS (Internet Information Service) version 5, avec ASP, ASP.NET
- MSIE (Microsoft Internet Explorer) version 6.0
- Environnement de développement : Visual Studio .NET
Qu'apporte la plateforme .NET?
D'après la littérature de Microsoft sur le sujet, .NET apporte deux éléments :
- L'environnement 'Common Language Runtime' (CLR - Environnement d'exécution du langage commun) ;
- Et la bibliothèque de classe '.NET Framework Class Library'
Ce qui m'a impressionné personnellement, était plutôt le
CLR, qui me paraissait être la nouveauté qui distingue réellement le produit…
La bibliothèque de classes, elle, est un peu du 'déjà vu' : une sacoche pleine de boulons livrés, pour la plupart, en vrac… beaucoup sont d'ailleurs assez anciens… lustrés pour l'occasion.
Du point de vue du développeur, le
Common Language Runtime (
CLR) apporte les nouveaux avantages suivants :
- Compilation du code indépendamment du langage de programmation utilisé, par l'implémentation d'un langage intermédiaire (Intermediate language) qui ouvre également des nouvelles possibilités de communication entre les modules et composantes des applications dites 'gérées' (qui tournent sous le CLR) ;
- Une meilleure 'robustesse' du code par l'implémentation d'une infrastructure de vérification stricte des types de données et du code, nommée : Common Type System (CTS) (Système Commun de Typage)
- Une meilleure gestion de la mémoire : élimination des indiscrétions (memory leaks) et des références invalides de mémoire (invalid memory references), gestion automatique de la libération de mémoire des objets inutilisés (garbage collection)… ;
- Amélioration des performances des scripts et du code précédemment interprété ;
- Une meilleure gestion de sécurité d'exécution du code : protection de l'utilisateur et de l'auteur du code ;
Pour un développeur sans 'héritage' cela peut être très bien…
Pour le vieux développeur qui se promène avec quelques millions de lignes de code à son 'actif'… le 'déménagement' n'ira pas sans quelques problèmes !
La bonne nouvelle est que Microsoft dit : «
bien que le runtime soit conçu pour le software du futur (!), il supportera celui d'aujourd'hui et d'hier »
La migration pourra donc se faire en douceur… du moins : sans panique !
SP à ASP.NET : Le chemin…
Avec ASP.NET nous sommes –enfin ! – en mesure d'écrire de 'vraies' applications web.
Les classes (avec héritage et polymorphisme), la vérification stricte de typage, la gestion des espaces de noms, la compilation du code… un vrai bonheur pour un programmeur.
Hélas… pour ces mêmes raisons, la migration de ASP vers ASP.NET n'est pas une question de conversion de fichiers ou de code !
Il s'agit, en effet, d'une migration d'approche et de méthode de travail.
Histoire d'une pagaille !
Il s'est passé beaucoup de choses dans les quinze dernières années.
La concurrence acharnée entre les producteurs des outils de développement a beaucoup fait évoluer l'environnement de travail du développeur.
L'entrée en force – presque soudaine ! – d'Internet dans la vie quotidienne des entreprises et des individus, et les intérêts économiques qui se sont associés, ont un peu bousculé cette évolution.
Dans l'urgence de répondre aux besoins des industriels, lancés à plein corps dans l'aventure d'Internet, l'environnement de développement s'est jeté dans une anarchie quasi irrationnelle… Le résultat est assez désastreux…
Des outils bâclés, gratuits ou payants, pour produire des milliards de pages web dont aucune ne s'affiche de la même manière sur deux machines !
Des graphistes qui passent leur temps à 'caler' les éléments de leurs pages avec des pixels vides (!!) ou à remplacer le texte des boutons par des images pour garantir un minimum de compatibilité entre les navigateurs (dont les fabricants sont en guerre !)…
Et des développeurs web qui doivent gérer des millions de lignes de code écrits souvent à la hâte et éparpillés en vrac dans des milliers de fichiers sans aucun lien…
Des environnements de développement incomplets et des langages de programmation troubles… c'est ainsi que la quasi-totalité des sites Internet, lorsqu'ils sont composés de plus 50 pages, peut être qualifiée d'ingérable !
Mon expérience personnelle s'est faite avec VBScript pour le code côté serveur, JavaScript pour le code côté client… j'en parlerai un peu plus loin…
L'environnement de développement lui était composé d'une multitude d'outils : Visual Interdev pour écrire le code (après quelques tentatives, j'ai du abandonner son utilisation pour le débogage !)… Front page pour composer les ébauches des tables complexes, SQL Entreprise Manager pour les rudes tâches de travail avec les bases de données… MS Access simplifiait parfois certaines de ces tâches… Mais, comme les éditeurs de requêtes SQL de ces deux outils étaient un peu rustres, j'avais recours à TextPad (mon éditeur fétiche de chez Helios) qui m'assistait dans les recherches et remplacements avant de recoller ensuite le texte résultant dans l'éditeur de requêtes en cours (navigation qui devait se faire plusieurs fois, car TextPad lui ne connaît rien sur le SQL ce qui produisait souvent des erreurs chez les autres voisins !!)
Il y avait, évidemment, d'autres problèmes à gérer (par d'autres outils !) comme par exemple les connexions aux serveurs (web, SQL…) souvent dans une ambiance assez houleuse en raison de l'humeur massacrante des techniciens d'hébergement (qui devaient, eux aussi, avoir leur bonne part dans la bataille !)
Le temps passé dans ces gymnastiques était, naturellement, pris sur la part du temps pour les tests et le débogage… ce qui finissait par produire des résultats 'insatisfaisants' qui, à leur tour, relançaient le même mécanisme infernal !
L'utilisation de VBScript et de JavaScript n'arrangeait rien à mon naturel 'dépressif' !
Étant des langages interprétés, il était impossible de tester la totalité des lignes de code. Une simple erreur de syntaxe dans une ligne de code n'étant détectable qu'au moment de l'exécution de celle-ci, il fallait 'provoquer' l'interpréteur pour lui faire exécuter toutes les lignes (disons : le maximum) pour simplement vérifier leur syntaxe…
Cela était très agaçant car c'était bêtement IMPOSSIBLE !
Exemple :
IF x = 5 THEN
Kaka.kak.aka!!+°°° '// erreur de syntaxe
END IF
Dans les lignes ci-dessus, tant que
x n'est pas
=5, l'erreur de syntaxe ne sera pas détectée (en général,
x devient
=5 en pleine présentation chez le client !!)
La syntaxe même de ces deux choses me laissait 'rêveur' !
Il me chagrinait beaucoup par exemple que les lignes suivantes puissent produire une erreur :
DIM x AS Integer
DIM y AS String
En effet, la syntaxe correcte était donc d'écrire seulement (sans préciser les types des variables !) :
DIM x
DIM y
Ce qui était plus facile à écrire, certes…
L'inquiétant était de savoir que le code suivant, bien que 'conforme' au langage, produira, sans doute, une 'vraie' erreur qui peut poser de sérieux problèmes :
x = 5
x = x +2 '// cela donne 7: ok
y = "bonjour"
x = y
x = x +2 '// et ceci peut, probablement, donner "bonjour2" !
On ne peut même pas 'initialiser' une variable… on ne peut que lui affecter une valeur.
Exemple : on ne peut pas écrire :
DIM x =5
Il faut faire cela en deux fois : déclarer la variable, puis lui affecter une valeur, comme ceci :
DIM x
X = 5
Avec de tels handicapes, on ne peut évidemment pas envisager la création de classes ni des structures (type dans VB)… les tentatives d'utilisation de ces techniques, notamment dans JavaScript, était une comédie sans nom !
Même la fausse élégance, empruntée du C++, dans JavaScript (les accolades, les '
++' et les '
--', et les commentaires avec '//') avait du mal à camoufler la véritable approche approximative (à la louche) du langage !
Dans des telles conditions, un seul fichier source de code devenait immanquablement ingérable dès que sa taille dépassait 300 lignes… et l'on devait gérer tout un site !
Microsoft se ressaisit !
Il fallait bien que quelqu'un (de taille !) intervienne pour que cette pagaille cesse ou, du moins, se canalise vers quelque chose de 'sensé'.
Avec .Net, Microsoft a finit apparemment par faire la première tentative sérieuse dans ce domaine.
Pour offrir une nouvelle plateforme de développement cohérente et raisonnablement accessible, il fallait surtout rompre avec l'anarchie de ces outils bâclés et faits à la hâte.
La pagaille est telle, que conserver une 'entière compatibilité' avec l'existant pouvait être une porte ouverte vers des pentes dangereuses.
C'est probablement la raison des différences notables entre ASP et ASP.NET…
Aperçu des antinomies ASP/ASP.NET
Dans ASP.NET une application web est un ensemble… un même espace composé de tous les fichiers de code et des différentes ressources d'interface utilisées par l'application.
L'ensemble des lignes du code est vérifié et compilé avant exécution… pour produire le fichier exécutable de l'application.
Fini les imprécisions agaçantes dans les déclarations des variables :
On ne peut plus écrire :
DIM x
Il faut maintenant (comme dans tout langage de programmation) préciser de quel type est cette variable, par exemple :
DIM x AS String
Il est également possible d'initialiser les variables :
DIM x AS String ="bonjour" '// OK nous sommes dans asp.net
Visibilité des variables et des fonctions
.NET offre également un mécanisme cohérent de visibilité des variables :
Dans ASP, pour accéder aux fonctions et variables déclarées dans un fichier, nous devions implicitement, 'inclure' ce fichier (avant toute ligne utilisant ces variables ou faisant appel à l'une de ces fonctions) comme ceci :
<!--#include file="globals.asp"-->
Il fallait être assez bien organisé et assez rigoureux pour distribuer ces 'include' de manière à avoir accès aux variables et fonctions nécessaires sans alourdir l'exécution de l'application (le fichier inclus est inséré à chaque ouverture du fichier contenant l'instruction 'include' !!)
L'application étant compilée, ces inclusions deviennent obsolètes dans ASP.NET.
Avec ASP.NET, toutes les variables déclarées dans un fichier source (module, ou module de classe) sont visibles seulement dans ce module.
Dans un module, la déclaration d'une variable peut cependant comporter un attribut de visibilité 'Public' qui la rend visible dans toute l'application (ou dans son '
espace de noms' (
Name space)).
Exemple : si l'on souhaite donner un accès global (dans tous les modules de l'application) à la variable
global_string déclarée dans un module, on peut écrire :
Public global_string As String
Les fonctions également (qui sont 'publiques' par défaut), peuvent être déclarées avec un attribut de visibilité particulier selon le nécessaire (Private, Protected…)
Dans ASP, pour ouvrir une connexion à une base de données, on pouvait écrire :
DIM connex '// on ne sait pas de quel type est connex.. pas grave!
DIM connect_string '// ceci sera la chaîne de connexion
'// on appel une fonction pour avoir la chaîne de connexion
connect_string =get_connection_string()
'// on appel le serveur pour nous créer une instance connexion ADODB
Set connex =Server.CreateObject("ADODB.CONNECTION")
'// on ouvre la connexion
connex.Open connect_string
Tout cela est assez hasardeux puisque la seule chose qui détermine que
conn est une connexion ADODB est le nom de l'objet
"adodb.connection" dans l'appel au serveur : Server.CreateObject("adodb.connection")
Une seule erreur de frappe dans "adodb.connection" donnerait un résultat désastreux, qui apparaîtra uniquement au moment de l'exécution !
Dans ASP.NET, les choses sont plus cohérentes dès la déclaration de la variable :
Dim connect_string AS String '// la chaîne de connexion
Dim connex As OleDb.OleDbConnection '// on déclare la connexion
'// on obtien la chaîne de connexion
connect_string = get_connection_string()
'// on crée une nouvelle instance (place mémoire) pour la connexion
'// qui est un objet (OleDb.OleDbConnection) d'une bibliothèque
connex = New OleDb.OleDbConnection(connect_string)
'// on ouvre la connexion
Connex.Open()
Cela aurait pu être encore plus court, puisque .NET nous permet d'initialiser les variables:
Dim connect_string AS String = get_connection_string()
Dim connex As New OleDb.OleDbConnection(connect_string)
Connex.Open()
Le 'nouveau' ici est le mot clé :
New qui crée une nouvelle instance d'un objet (réservation mémoire avec les initialisations nécessaires des éléments propres à l'objet concerné).
Comme le savent bien les programmeurs C++ : selon son implémentation, un objet (classe) peut permettre une ou plusieurs variantes de 'constructeurs' pour créer une instance.
L'objet OleDb.oleDbConnection, par exemple, nous permet de créer une nouvelle instance sans paramètres à fournir :
connex = New OleDb.OleDbConnection()
Ou en fournissant la chaîne de connexion :
connex = New OleDb.OleDbConnection(connect_string)
Selon les paramètres fournis (leur nombre, et types de données dans l'ordre),
New fait appel au 'constructeur' adéquat de l'Objet concerné.
Si l'objet en question ne fournit pas de constructeur correspondant au nombre et types de paramètres fournis, le compilateur se charge de nous adresser un message d'erreur… ouf… nous somme sauvés !
Cela nous sera très utile lorsque nous allons créer nos classes plus tard.
Une classe est un espace spécialisé pour le traitement d'un objet.
Une classe 'date', par exemple, peut être un espace spécialisé dans le traitement des dates : fournir le jour de la semaine d'une date, calculer la date après n jours, mois ou années à partir d'une date… peut être aussi fournir la phase de la lune à une date donnée… etc.
L'avantage de l'utilisation des classes est de perfectionner le traitement d'un objet, et, en même temps, d'isoler le code nécessaire à ses traitements.
Isoler le code nécessaire au traitement d'un objet est un avantage particulièrement intéressant :
- Il permet de construire des composantes réutilisables ;
- Il permet de localiser plus facilement les erreurs dans une application ;
Un avantage assez intéressant des classes, est également celui de permettre une écriture assez claire du code :
mon_jour =date1.jour
mon_mois =date1.mois
date2 =date1.ajoute_jours(156)
phase_lune =date2.phase_lune()
Les membres d'une classe : propriétés / méthodes / fonctions
Une classe renferme les informations et les fonctionnalités liées au traitement de l'objet qu'elle présente.
Cela peut comprendre des données représentant les attributs de l'objet ou pouvant être nécessaires à ses traitements…
Une classe propose, généralement, un set de fonctions qui fournissent des informations sur les attributs de l'objet, qui affectent les valeurs de certains de ces attributs et/ou qui font des calculs spécifiques à l'objet.
Les données déclarées dans l'espace (fichier) d'une classe sont par défaut visible seulement à l'intérieur de la classe. Cette limitation de visibilité permet à la classe de contrôler la cohérence de ses attributs selon les critères interne de l'objet. (Permet, par exemple, à un objet date de contrôler que la date ne soit jamais le 31 février).
Les données d'une classe peuvent avoir l'attribut 'Public', 'Protected' ou 'Friend'.
Une variable de classe ayant l'attribut 'Public' sera directement accessible par toutes les fonctions (dans le même projet ou dans les projets qui référenceraient celui-ci) lors de la manipulation d'une instance de l'objet représenté par la classe. Une variable 'Public' d'une classe est un 'champ' (field) de la classe et sa valeur pourra être directement modifiée par l'utilisateur.
Une variable de classe ayant l'attribut 'Protected' sera accessible seulement par les fonctions membres de la classe ou d'une classe dérivée de la classe (nous verrons la dérivation des classes plus loin).
Une variable de classe ayant l'attribut 'Friend' sera accessible par toutes les fonctions dans le même projet lors de la manipulation d'une instance de l'objet de la classe.
Toute variable n'ayant pas d'attribut lors de sa déclaration, est considérée comme ayant l'attribut 'Private' et sera accessible seulement par les fonctions membres de la classe.
Exemple de déclaration d'une classe de tag (étiquette) html
Nous allons déclarer une classe qui représentera un tag html.
Notre classe de tag contiendra quatre informations :
- Le nom du tag html – chaîne de caractères (par exemple : TD, P, SPAN… etc.)
- Le style du tag – chaîne de caractère ;
- Les autres informations du tag ;
- Et le texte associé au tag.
Public Class html_tag '// notre classe est publique
'// son nom est : html_tag
'-------------------------------
' attributs de la classe
'-------------------------------
Protected m_html_tag As String '// ex: td, span, p...
Protected m_html_style As String '// style html du tag
Protected m_info As String '// autres infos
Protected m_text As String '// texte associé au tag
'-------------------------------
' ici.. on écrira, par la suite,
' les propriétés et les fonctions
' de la classe
'-------------------------------
End Class
Comme vous avez remarqué, les attributs du tag sont '
Protected' : accessible seulement dans la classe et dans les classes qui pourraient en dériver.
Nous allons maintenant, dans la zone réservée ci-dessus, proposer des propriétés qui permettront à l'utilisateur de notre classe d'obtenir et de mettre à jour les valeurs de ces attributs :
'-----------------------------------------------
' propriété: obtient ou met à jour le tag html
'-----------------------------------------------
Property tag() As String
Get
Return (m_html_tag)
End Get
Set(ByVal Value As String)
m_html_tag = Value
End Set
End Property
'-----------------------------------------------
' propriété: obtient ou met à jour le style html
'-----------------------------------------------
Property style() As String
Get
Return (m_html_style)
End Get
Set(ByVal Value As String)
m_html_style = Value
End Set
End Property
'-----------------------------------------------
' propriété: obtient ou met à jour les 'autres infos'
'-----------------------------------------------
Property info() As String
Get
Return (m_info)
End Get
Set(ByVal Value As String)
m_info = Value
End Set
End Property
'-----------------------------------------------
' propriété: obtient ou met à jour le texte associé
'-----------------------------------------------
Property text() As String
Get
Return (m_text)
End Get
Set(ByVal Value As String)
m_text = Value
End Set
End Property
Il faut maintenant proposer une fonction qui écrit le tag avec son texte associé.
La fonction 'écrira' dans un objet '
HttpResponse'… c'est-à-dire au moment du transfert des données vers le navigateur du poste client, l'objet
HttpResponse sera un paramètre fournit par l'appelant et accessible par référence (
ByRef) pour que la fonction puisse y écrire les informations :
'-----------------------------------------------
' méthode: écrire le tag et le texte associé
'-----------------------------------------------
Sub output(ByRef response As HttpResponse)
response.Write("<" & m_html_tag) '// ouvre le tag
'// écrire les attribus (s'il en existe)
If Len(m_info) > 0 Then
response.Write(" " & m_info)
End If
'// écrire le style html (s'il en existe)
If Len(m_html_style) >0 Then
response.Write( " Style=""" & m_html_style & """)
End If
response.Write(">") '// fermer le tag
'// écrire le texte associé (s'il en existe)
If Len(m_text) >0 Then
response.Write( m_text)
End If
'// écrire fin de tag (ex : </span>)
response.Write( "</" & m_html_tag &">")
End Sub
Les Constructeurs / Destructeurs
Tout cela est très bien, notre classe est presque parfaite… c'est un nouveau type de données que l'on peut utiliser dans une déclaration comme celle-ci :
Dim mon_tag As html_tag
Pour utiliser les options de cette classe, il nous faudra créer une instance de la classe : c'est-à-dire utiliser la méthode
New.
Pour créer une instance d'une classe, celle-ci doit avoir une (ou plusieurs) méthodes
New : les constructeurs de la classe.
De même, lors de la libération de la mémoire utiliser par une instance, une classe peut proposer au système une méthode '
Finalize'
: destructeur de la classe, qui s'occupera des opérations de nettoyage éventuelles (notamment pour libérer la mémoire occupée par des données internes à la classe…) avant de libérer la mémoire de l'instance en cours de la classe.
Nous allons maintenant, toujours dans la zone que nous avons réservée pour les fonctions et propriétés, proposer deux versions de la méthode
New :
La première sera sans paramètres (et sera donc la méthode par défaut pour créer une instance de notre classe)… elle initialisera le tag html à <P>
Sub New()
m_html_tag = "P"
End Sub
Notre deuxième version de
New aura, comme paramètre, le nom du tag html souhaité :
Sub New(ByVal str_html_tag As String)
m_html_tag = str_html_tag
End Sub
Avec ces deux versions de
New, il nous sera possible d'écrire :
Dim mon_tag As New html_tag() '// créer un tag par défaut (<P>)
Ou d'écrire:
Dim mon_tag As New html_tag("span") '// créer un tag <span>
Les valeurs par défaut des paramètres
Les deux versions de
New que nous venons d'écrire ont un sens simple : si l'utilisateur ne précise pas le nom du tag, nous utiliserons le tag "P" par défaut.
ASP.NET propose une simplification de ce type de situation, simplification héritée du C++, et qui nous permet d'écrire une seule méthode
New avec une valeur par défaut pour le paramètre tag (si celui-ci est absent) :
Sub New(Optional ByVal str_html_tag As String ="P")
m_html_tag = str_html_tag
End Sub
Cette nouvelle version de
New comporte les deux versions précédentes en un seul bloc.
Bien entendu, déterminer les valeurs par défaut des paramètres est applicable sur toutes les propriétés, fonctions ou méthodes dans une classe, et également sur toutes les fonctions des modules.
Superposition des méthodes (
Overloading)
Le fait de pouvoir écrire plusieurs versions (avec des paramètres différents et/ou type de données renvoyé différent) d'une même méthode est appelé '
overloading' – superposition.
Nous pouvons, par exemple, écrire une nouvelle version de notre méthode
New qui prendra comme paramètre une autre instance d'objet
html_tag. La méthode se chargera de dupliquer les informations de l'objet fourni dans la nouvelle instance :
Sub New(ByVal autre_tag As html_tag)
m_html_tag = autre_tag.m_html_tag
m_html_style = autre_tag.m_html_style
m_text = autre_tag.m_text
End Sub
La superposition (
overloading) est applicable sur les méthodes des classes et également sur toutes les fonctions des modules.
On peut, par exemple, écrire dans un module deux versions d'une fonction
max :
Function max(ByVal long1 As Long, ByVal long2 As Long) As Long
If long1 > long2 Then
Return long1
Else
Return long2
End If
End Sub
Function max(ByVal int1 As Integer, ByVal int2 As Integer) As Integer
If int1 > int2 Then
Return int1
Else
Return int2
End If
End Sub
Le compilateur se charge de :
- À la création des fonctions superposées : de vérifier que les fonctions superposées (overloaded) sont réellement différentes et adresse une erreur s'elles sont identiques (ayants des paramètres de types identiques et ayants un type de retour identique) ;
- Au moment de l'appel à une fonction superposée : déterminer la version adéquate de la fonction à appeler selon les paramètres fournis, et adresse une erreur s'il ne trouve pas de fonction correspondant au contexte de ces paramètres.
Dériver les classes est une technique qui permet de construire une architecture hiérarchisée d'objets ayant des attributs communs représentés par une classe de base.
Notre classe html_tag, par exemple, peut être la classe de base de certaines sous-classes :
html_tag définit un tag html de base contenant le nom du tag, son style et un texte associé…
Une cellule de table (<td>) est également un tag html, mais qui peut avoir besoin de plus d'informations pour être dessiné : sa largeur, hauteur, alignement…
<td> est donc un bon candidat pour une sous-classe de html_tag.
Héritage des classes (inheritance)
Notre sous-classe <td>, appelons la '
html_tag_td', est une '
html_tag' avec quelques propriétés et méthodes supplémentaires.
Nous allons, dans une nouvelle classe, 'hériter' les propriétés et méthodes de html_tag, en ajoutant les informations spécifiques au tag <td> :
Public Class html_tag_td '// notre classe est publique
'// son nom est : html_tag_td
Inherits html_tag '// nous héritons html_tag
'-------------------------------
' attributs spécifiques à la classe
'-------------------------------
Protected m_largeur As String '// ex: "30%"
'-------------------------------
' ici les propriétés et méthodes
' de la classe
'-------------------------------
End Class
Hériter
html_tag, permet à toutes les fonctions de la nouvelle classe (
html_tag_td) d'accéder aux variables et de faire appel aux fonctions et méthodes qui sont déclarées dans la classe de base
html_tag.
Comme pour toutes les classes, nous allons définir une méthode
New qui se chargera de créer une nouvelle instance de notre
html_tag_td :
Sub New(optional ByVal largeur As String ="15%")
MyBase.New("td") '// appeler html_tag pour un tag "td"
m_largeur = largeur '// largeur, par défaut "15%"
End Sub
Écrivons maintenant les propriétés spécifiques à la classe :
'-----------------------------------------------
' propriété: obtient ou met à jour la largeur
'-----------------------------------------------
Property largeur() As String
Get
Return (m_largeur)
End Get
Set(ByVal Value As String)
m_largeur = Value
End Set
End Property
Outrepasser les méthodes et fonctions (
overriding)
Vous avez probablement remarqué que nous avons un petit problème : comment allons nous traiter la méthode
output définie dans la classe de base ?
La méthode
output de
html_tag écrit le tag, son style, son texte associé puis fin de tag… elle ne tient pas compte de la nouvelle information que nous avons : la largeur !
ASP.NET fournit une solution (héritée encore du C++ !) :
outrepasser la méthode définie dans la classe de base…
Outrepasser une méthode c'est : définir une méthode ayant le même nom et les mêmes paramètres pour effectuer le même traitement mais de manière adaptée à la situation de notre nouvelle classe.
Mais, pour outrepasser une méthode dans une classe, il faut que la méthode de la classe de base soit déjà déclarée comme permettant d'être outrepassée (
overridable) !
Nous allons donc modifier la déclaration de la méthode
output de la classe
html_tag avant d'écrire celle de remplacement :
'-----------------------------------------------
' méthode: écrire le tag et le texte associé
' Overridable = peut être outrepassée
'-----------------------------------------------
Overridable Sub output(ByRef response As HttpResponse)
On peut maintenant écrire notre nouvelle méthode qui tiendra compte de l'élément largeur :
'-----------------------------------------------
' méthode: écrire le tag td (et le texte associé)
' overrides = outrepasse celle de la classe html_tag
'-----------------------------------------------
Overrides Sub output(ByRef response As HttpResponse)
response.Write("<" & m_html_tag) '// ouvre le tag
'// écrire les attribus (s'il en existe)
If Len(m_info) > 0 Then
response.Write(" " & m_info)
End If
'// écrire le style html (s'il en existe)
If Len(m_html_style) >0 Then
response.Write( " Style=""" & m_html_style & """")
End If
'// écrire la largeur (s'il en existe)
If Len(m_largeur) >0 Then
response.Write( " width=""" & m_largeur & """")
End If
response.Write(">") '// fermer le tag
'// écrire le texte associé (s'il en existe)
If Len(m_text) >0 Then
response.Write( m_text)
End If
'// écrire fin de tag (ex : </span>)
response.Write( "</" & m_html_tag &">")
End Sub
Le code que nous venons d'écrire pour la méthode
output est un peu redondant avec celui de la classe de base !
Pour traiter ce problème, nous allons séparer la méthode de la classe de base (
html_tag) en trois méthodes : une qui écrit l'en-tête du tag, la deuxième pour écrire le texte associé au tag, et la dernière pour écrire la fin du tag… la méthode
output (qui restera
overridable) fera appel à ces trois méthodes dans l'ordre.
'-----------------------------------------------
' méthode: écrire l'en-tête du tag
'-----------------------------------------------
Sub output_head(ByRef response As HttpResponse)
response.Write( vbCrLf & "<" & m_html_tag) '// ouvre le tag
'// écrire les autres infos (s'il en existe)
If Len(m_info) > 0 Then
response.Write(" " & m_info)
End If
'// écrire le style html (s'il en existe)
If Len(m_html_style) > 0 Then
response.Write( " Style=""" & m_html_style & """")
End If
End Sub
'-----------------------------------------------
' méthode: écrire le texte du tag
'-----------------------------------------------
Sub output_text(ByRef response As HttpResponse)
If Len(m_text) > 0 Then
response.Write( m_text)
End If
End Sub
'-----------------------------------------------
' méthode: écrire la fin de tag
'-----------------------------------------------
Sub output_foot(ByRef response As HttpResponse)
response.Write( "</" & m_html_tag &">")
End Sub
'-----------------------------------------------
' méthode: écrire l'ensemble des infos du tag
' Overridable = peut être outrepassée
'-----------------------------------------------
Overridable Sub output(ByRef response As HttpResponse)
output_head(response)
response.Write(">") '// fermer le tag
'// écrire le style html (s'il en existe)
output_text( response)
output_foot( response)
End Sub
De cette manière, la méthode output de notre nouvelle classe
html_tag_td sera plus concise :
'-----------------------------------------------
' méthode: écrire le tag td (et le texte associé)
' overrides = outrepasse celle de la classe html_tag
'-----------------------------------------------
Overrides Sub output(ByRef response As HttpResponse)
output_head(response)
'// écrire la largeur (s'il en existe)
If Len(m_largeur) >0 Then
response.Write( " width=""" & m_largeur & """")
End If
response.Write(">") '// fermer le tag
output_text( response)
output_foot( response)
End Sub
Les formulaires web (Web forms)
Les formulaires web sont les pages
.aspx, similaires aux pages
.asp dans les sens qu'elles peuvent être associées à du code exécuté sur le serveur pour produire une partie ou la totalité du contenu 'affichable' de la page au moment de la transmission de celle-ci vers le navigateur du poste client.
Avec ASP, le code associé à la page
.asp était composé d'un amalgame de morceaux de code importés dans la page grâce à la fameuse instruction
<!--#include file="xx"-->, ou insérés directement dans la page, soit par l'utilisation des miraculeux signes
<% et
%>, entre lesquels on pouvait écrire toute sorte de chose qu'un langage de programmation peut permettre !... soit, de manière plus académique, en utilisant
<script runat="server" language="xx">
Avec les web forms, ASP.NET propose une architecture plus cohérente :
- Par la séparation du code associé à une page de son aspect visuel ;
- Par un modèle évènementiel de traitement du code (par contraste au modèle linéaire de ASP qui consistait à traiter séquentiellement les lignes de la page (html ou code !) du haut vers le bas)
Un formulaire web est en réalité une classe qui peut produire des éléments visuels en utilisant comme support, la trame graphique de base de la page
.aspx physique.
Le code de la classe d'un web form, qui peut être écrit dans l'un des langages proposés par la plateforme .NET (
VB, C# ou JScript.Net), est compilé faisant partie de l'application et sera exécuté sur le serveur. Il est stocké dans un fichier (
.aspx.vb lorsqu'il s'agit de code VB) indépendamment physiquement du fichier 'support visuel'
.aspx.
Étant exécuté sur le serveur, le code d'une classe web form bénéficie de toutes les options de la plateforme .NET et peut avoir accès aux objets proposées dans la bibliothèque de classe '
.NET Framework Class Library'
L'environnement de développement
Avec Microsoft Development Environment de Visual Studio.NET, Microsoft s'est, enfin, décidé d'offrir un environnement de développement réellement intégré.
Il est maintenant possible d'utiliser le même environnement de développement quelque soit le type du projet (local, web, SQL…), le langage de programmation utilisé (C/C++, VB, C#...) ou les ressources ou autres fichiers à manipuler (éditeur html, dialogues, icônes, images, éditeur hexadécimal…)
Il est également possible d'utiliser l'environnement pour obtenir des informations concernant le serveur ou la station de travail de son choix.
L'éditeur de code 'Intellisens' (qui affiche les membres d'une classe ou les paramètres de la fonction par exemple) est un excellent guide informatif en plus de la facilité d'écriture qu'il procure.
Un système d'aide dynamique étonnant qui distingue excellemment le contexte d'un mot recherché pour afficher la page en rapport dans le manuel : exemple : tapez F1 avec le curseur sur le mot 'protected' dans un contexte ASP.NET, faîtes la même chose dans le contexte C/C++.
Grâce à cet excellent environnement, le cycle de développement est beaucoup plus rapide que dans le 'passé' !
Réussir la migration vers ASP.NET
Comme nous venons de voir, la plateforme .NET représente des avantages indéniables du point de vue du développeur en général et du développeur web en particulier.
En même temps, pour une application web, bénéficier des avantages de .NET nécessite une réelle réorientation de l'architecture et du code de l'application.
La migration vers ASP.NET doit donc être considérée en tant que migration 'importante' qui demande à être correctement planifiée.
Une ébauche d'un plan de migration peut être :
- Mieux connaître ASP.NET : consulter la littérature, expérimenter l'outil ;
- Examiner l'existant : faire l'inventaire de l'existant et produire une première évaluation des effets (avantages / inconvénients) d'une migration ASP.NET sur les différents éléments ;
- Cristalliser les connaissances acquise à travers la maturité du projet objet de la migration : élaborer une vue de l'existant après une migration vers .NET (quelles classes utiliser, quelles optimisations, quelles extensions…)
- Déterminer les connaissances requises, planifier la formation : comment acquérir les compétences nécessaires, quels impacts sur les structures locales de travail ;
- Planifier et gérer la transition : déterminer les étapes et les calendriers de la migration.
Exemple de la migration de code : les onglets
Nous allons examiner un exemple pratique de la migration d'une section de code qui traite les onglets.
Le code dessine les onglets et se charge de traiter les événements associés aux changements de l'onglet actif.
Tout d'abord, nos onglets sont composés de :
- Une table contenant deux lignes ;
- Chaque cellule de la première ligne affiche un onglet ;
- La deuxième ligne est composée d'une seule cellule pour 'souligner' les onglets ;
- La table est suivie par une série de tables (une table pour chaque onglet), composée chacune d'une seule cellule qui contient un <iframe> qui affiche le contenu (page) associé à l'onglet correspondant ;
Figure 1 : schéma de la table des onglets
Un espace (table + iframe) est associé à chaque onglet pour afficher son contenu.
Les tables associées aux onglets (nommons les : table1, table2 et table3) sont cachées ou affichées selon l'onglet sélectionné :
- Lorsque l'onglet 1 est actif : table1 est affichée, table2 et table3 sont cachées ;
- Lorsque l'onglet 2 est actif : table2 est affichée, table1 et table3 sont cachées ;
- …
Lorsque l'onglet 1 est actif, les éléments auraient l'apparence suivante :
Figure 2 : apparence avec onglet 1 actif
Si l'utilisateur clique l'onglet 2, les éléments auraient l'apparence :
Figure 3 : apparence avec 'onglet 2' actif
En activant l'onglet 3 :
Figure 4 : apparence avec 'onglet 3' actif
Ce traitement est effectué sur le poste client par une procédure qui répond à l'événement OnClick de l'onglet.
Les onglets : le résultat côté client
Figure 5 : exemple d'onglets dans le navigateur
Matériellement, dans le fichier html transmis au navigateur du poste client, les onglets de la
Figure 5 ci-dessus ressemblent à ceci :
<table id="idTabs" name="idTabs" class="tab_table"
width:"100%;" cellspacing="0" cellpadding="0" >
<tbody>
<!—---------------les onglets ---------------->
<tr>
<td id="onglet0" name="onglet0" class="nTabSelected"
width="7%" height="22" title="dossiers du site" nowrap
onclick="ontabClick(0,'nTab','nTabSelected')">Dossiers</td>
<td id="onglet1" name="onglet1" class="nTab"
width="7%" height="22" title="pages du site" nowrap
onclick="ontabClick(1,'nTab','nTabSelected')">Pages</td>
<td id="onglet2" name="onglet2" class="nTab"
width="7%" height="22" title="images du site" nowrap
onclick="ontabClick(2,'nTab','nTabSelected')">Images</td>
<td id="onglet3" name="onglet3" class="nTab"
width="7%" height="22" title="menu de gestion du site" nowrap
onclick="ontabClick(3,'nTab','nTabSelected')">Menus SM</td>
</tr>
<!—--------------- pied des onglets ---------------->
<tr height="0">
<td class="nTabBottom" colSpan="4" style="width:100%;"> </td>
</tr>
</tbody>
</table>
<!—-------------------------------------------------------------->
<!—--------------- contenus associés aux onglets ---------------->
<!—-------------------------------------------------------------->
<!—--------------- contenu associé à l'onglet 0 ---------------->
<table id="txt_Content0" name="txt_Content0"
style=" visibility:visible; display:block;" width="100%" height="95%"
valign="top" cellspacing=0 cellpadding=0 border=0 >
<tr>
<td width="100%" height="100%" nowrap>
<iframe id="nav_frame0" name="nav_frame0" style="LEFT:0px; TOP:0px;
width:100%; height:100%;
visibility:visible; display:block;"
width="100%" height="100%"
src="pages/folders.htm"
scrolling="no" marginheight="0" marginwidth="0"
framespacing="0" ></iframe>
</td>
</tr>
</table>
<!—--------------- contenu associé à l'onglet 1 ---------------->
<table id="txt_Content1" name="txt_Content1"
style=" visibility:hidden; display:none;" width="100%" height="95%"
valign="top" cellspacing=0 cellpadding=0 border=0 >
<tr>
<td width="100%" height="100%" nowrap>
<iframe id="nav_frame1" name="nav_frame1" style="LEFT:0px; TOP:0px;
width:100%; height:100%;
visibility:hidden; display:none;"
width="100%" height="100%"
src="pages/pages.htm"
scrolling="no" marginheight="0" marginwidth="0"
framespacing="0" ></iframe>
</td>
</tr>
</table>
<!—--------------- contenu associé à l'onglet 2 ---------------->
<table id="txt_Content2" name="txt_Content2"
style=" visibility:hidden; display:none;" width="100%" height="95%"
valign="top" cellspacing=0 cellpadding=0 border=0 >
<tr>
<td width="100%" height="100%" nowrap>
<iframe id="nav_frame2" name="nav_frame2" style="LEFT:0px; TOP:0px;
width:100%; height:100%;
visibility:hidden; display:none;"
width="100%" height="100%"
src="pages/images.htm"
scrolling="no" marginheight="0" marginwidth="0"
framespacing="0" ></iframe>
</td>
</tr>
</table>
...
...
Lorsque l'utilisateur clique sur la cellule d'un onglet, la procédure
ontabClick est appelée.
Cette procédure, toujours dans le fichier html du navigateur, ressemble à ceci :
<script language="javascript">
<!--
var last_ndx =0;
function ontabClick( ndxTab, class_tab, class_tab_selected )
{
// si l'élément est déjà actif: fin
if( last_ndx ==ndxTab)
return;
// déclarer les variables
var last_tab, cur_tab,
last_frame, cur_frame;
// objets à activer, ex : "onglet2", "txt_Content2", "nav_frame2"
cur_tab =document.all("onglet" + parseInt( ndxTab));
cur_table =document.all("txt_Content" + parseInt( ndxTab));
cur_frame =document.all("nav_frame" + parseInt( ndxTab));
// objets à désactiver, ex : "onglet0", "txt_Content0", "nav_frame0"
last_tab =document.all("onglet" + parseInt( last_ndx));
last_table =document.all("txt_Content" + parseInt( last_ndx));
last_frame =document.all("nav_frame" + parseInt( last_ndx));
cur_tab.className =class_tab_selected; // modifier l'apparrence
last_tab.className =class_tab; // des onglets actif / inactif
// cacher la table et le iframe actuellement actifs
if( last_frame !=null)
{
last_frame.style.display ="none";
last_frame.style.visibility ="hidden";
}
if( last_table !=null)
{
last_table.style.display ="none";
last_table.style.visibility ="hidden";
}
// afficher la table et le iframe de l'onglet à activer
if( cur_frame !=null)
{
cur_frame.style.display ="block";
cur_frame.style.visibility ="visible";
}
if( cur_table !=null)
{
cur_table.style.display ="block";
cur_table.style.visibility ="visible";
}
// sauvegarder l'index du dernier onglet actif
last_ndx =ndxTab;
}
//-->
</script>
Figure 6 : après activation de l'onglet 'Images' dans le navigateur
Le travail côté serveur avec ASP.NET
Comme nous le savons, le code ainsi transmis vers le navigateur du poste client est le résultat d'un travail, parfois fastidieux !... effectué sur le serveur.
Voici comment, avec ASP.NET nous pouvons effectuer ce travail sur le serveur.
Nous allons nous intéresser à nos classes
html_tag et
html_tag_td (voir
Exemple de déclaration d'une classe de tag (étiquette) html plus haut)
Un onglet est en effet une cellule de table avec quelques informations complémentaires :
- Son index (ordre dans la série d'onglets) ;
- Ses attributs html (classe de la feuille de styles : actif / inactif) ;
- Le document (ou page) à afficher.
Nous allons déclarer une classe
html_onglet dérivée de la classe
html_tag_td :
Public Class html_onglet
Inherits html_tag_td '// nous héritons html_tag_td
'-------------------------------
' attributs de la classe
'-------------------------------
Protected m_index As Integer '// ordre
Protected m_css_class As String '// style inactif
Protected m_css_class_selected As String '// style actif
Protected m_doc_src As String '// page du contenu à afficher
'-------------------------------
' ici.. on écrira les propriétés
' et les méthodes de la classe
'-------------------------------
End Class
Nous allons ajouter deux versions de la méthode
New…
Une première version avec seulement l'index de l'onglet :
Sub New(ByVal index As Integer)
MyBase.New()
m_index = index
m_css_class = "nTab"
m_css_class_selected = "nTabSelected"
m_text = " " '// caption =par défaut, un espace
End Sub
La deuxième version de
New, avec plus de paramètres dont certains avec des valeurs par défaut :
Sub New(ByVal index As Integer, ByVal str_caption As String, _
ByVal str_doc_src As String, _
Optional ByVal str_css_class As String ="nTab", _
Optional ByVal str_css_class_selected As String ="nTabSelected")
MyBase.New()
m_index = index
m_doc_src = str_doc_src
m_css_class = str_css_class
m_css_class_selected = str_css_class_selected
m_text = str_caption
End Sub
On ajoute aussi les propriétés qui permetteront aux gens d'utiliser notre classe :
'// obtenir ou modifier l'index
Property index() As Integer
Get
Return (m_index)
End Get
Set(ByVal intVal As Integer)
m_index = intVal
End Set
End Property
'// obtenir ou modifier le document source
Property doc_src() As String
Get
Return (m_doc_src)
End Get
Set(ByVal Val As String)
m_doc_src = Val
End Set
End Property
'// obtenir ou modifier le style normal
Property css_class() As String
Get
Return (m_css_class)
End Get
Set(ByVal Val As String)
m_css_class = Val
End Set
End Property
'// obtenir ou modifier le style sélectionné
Property css_class_selected() As String
Get
Return (m_css_class_selected)
End Get
Set(ByVal Val As String)
m_css_class_selected = Val
End Set
End Property
Note : d'autres propriétés, comme la largeur de l'onglet, sont déjà gérées par notre classe de base
html_tag_td.
On réécrit la méthode
output de notre classe de base, car nous avons besoin de préciser l'index de l'onglet, sa classe de la feuille de style et de redériger le traitement de l'événement
OnClick :
'-----------------------------------------------
' méthode: écrire l'onglet
'-----------------------------------------------
Overrides Sub output(ByRef response As HttpResponse)
output_head(response)
'// écrire le id et nom de l'onglet
response.Write( " id=""onglet" & m_index & """")
response.Write( " name=""onglet" & m_index & """")
'// écrire la classe de la feuille de styles
If m_index =0 Then
response.Write( " class=""" & m_css_class_selected & """")
Else
response.Write( " class=""" & m_css_class & """)
End If
'// redériger l'événement OnClick de l'onglet
response.Write( " onclick="'ontabClick(" _
& m_index & "," _
& """" & m_css_class & """," _
& """" & m_css_class_selected & """);'")
'// écrire la largeur (s'il en existe)
If Len(m_largeur) >0 Then
response.Write( " width=""" & m_largeur & """")
End If
response.Write(">") '// fermer le tag
output_text( response)
output_foot( response)
End Sub
Il faut aussi qu'un onglet 'sache' écrire, à l'emplacement souhaité, une table contenant le <iframe> qui affichera le document source qui lui est associé.
Nous allons ajouter une méthode qui permet de faire cela :
'-----------------------------------------------
' méthode: écrire la table et le <iframe>
' du document associé à l'onglet
'-----------------------------------------------
Sub output_doc_frame(ByRef response As HttpResponse)
Dim tag_table As New html_tag("table")
Dim tag_iframe As New html_tag("iframe")
tag_table.info = " id=""txt_Content" & m_index & """ " _
& " name=""txt_Content" & m_index & """ " _
& " width=""100%"" height=""100%"" " _
& " valign=""top"" cellspacing=""0"" _
& " cellpadding=""0"" border=""0"" "
tag_iframe.info = " id=""nav_frame" & m_index & """ " _
& " name=""nav_frame" & m_index & """ " _
& " width=""100%"" height=""100%"" " _
& " src=""" & m_doc_src & """ " _
& " scrolling=""auto"" " _
& " marginheight=""0"" " _
& " marginwidth=""0"" " _
& " framespacing=""0"" "
'// le premier onglet est par défaut visible
If m_index = 0 Then
tag_table.style = "visibility:visible; display:block;"
tag_iframe.style = "LEFT:0px; TOP:0px; " _
& " width:100%; height:100%; " _
& " visibility:visible; display:block;"
Else
'// les autres onglets sont invisibles
tag_table.style = "visibility:hidden; display:none;"
tag_iframe.style = "LEFT:0px; TOP:0px; " _
& " width:100%; height:100%; " _
& " visibility:hidden; display:none;"
End If
tag_table.output_head(response)
response.Write(">")
response.Write(vbCrLf & "<tr>" _
& vbCrLf & "<td width=""100%"" height=""100%"" >")
tag_iframe.output(response)
response.Write(vbCrLf & "</td>" & vbCrLf & "</tr>" & vbCrLf)
tag_table.output_foot(response)
response.Write(vbCrLf)
End Sub
La classe '
onglets' (un set d'onglets)
Un 'set' d'onglets est une liste (collection) d'onglets (
html_onglet) :
Nous déclarons la classe :
Public Class onglets
'-------------------------------
' attributs de la classe
'-------------------------------
Private m_onglets As Collection '// liste de html_onglet
'-------------------------------
' ici.. on écrira les propriétés
' et les méthodes de la classe
'-------------------------------
End Class
Lors de la création d'une instance de la nouvelle classe
onglets, nous devons créer une instance (réserver de la mémoire) pour notre collection. Cela veut naturellement dire : que lorsque l'instance de la classe est libérée, nous devons libérer la mémoire occupée par cette collection. Nous allons ajouter les deux méthodes
New et
Finalize à notre classe
onglets :
Sub New()
m_onglets = New Collection()
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
'// libérer les onglets de la liste
n_onglets = m_onglets.Count()
For ndx = 1 To n_onglets
onglet = m_onglets(1)
m_onglets.Remove(1)
onglet = Nothing
Next
'// libérer la liste
m_onglets = Nothing
End Sub
Il faut que notre classe
onglets puisse permettre l'ajout d'un nouvel onglet:
'-----------------------------------------------
' méthode: ajouter un onglet
'-----------------------------------------------
Sub add_tab( ByVal index As Integer, ByVal caption As String, _
ByVal doc_src As String)
Dim new_tab As New html_onglet( index, caption, doc_src)
M_onglets.Add(new_tab)
End Sub
Il serait aussi assez utile de pouvoir accéder aux onglets de la collection avec une notation simple, comme par exemple :
mes_ongles(1)…
mes_onglets(2)… etc.
Nous allons écrire une propriété qui renvoie l'onglet de la collection à l'index souhaité.
Pour que cette écriture puisse être possible, nous allons déclarer cette propriété comme étant la propriété par défaut de notre classe :
'-----------------------------------------------
' propriété par défaut: accéder à l'onglet(ndx)
'-----------------------------------------------
Default ReadOnly Property Item(ByVal ndx As Integer) As html_onglet
Get
Return (m_onglets.Item(ndx))
End Get
End Property
D'autres propriétés utiles :
'-----------------------------------------------
' propriété: nombre dd'onglets de la collection
'-----------------------------------------------
ReadOnly Property count() As Integer
Get
Return (m_isoTabs.Count())
End Get
End Property
Nous allons aussi proposer une méthode qui écrira la procédure JavaScript qui gère l'événement
OnClick sur les onglets :
'-----------------------------------------------------
'* méthode privée : écrire le code JavaScript de la procédure OnClick
'-----------------------------------------------------
Private Sub output_toggle_tab_code(ByRef response As HttpResponse)
If m_onglets.Count() <= 1 Then
Exit Sub
End If
response.Write( vbCrLf & "<script language=""javascript"">" _
& vbCrLf & "<!--" _
& vbCrLf)
response.Write( _
vbCrLf & "var last_ndx =0;" _
& vbCrLf & "" _
& vbCrLf & "" _
& vbCrLf & "function ontabClick( ndxTab, class_tab, class_tab_selected )" _
& vbCrLf & "{" _
& vbCrLf & " if( last_ndx ==ndxTab)" _
& vbCrLf & " return;" _
& vbCrLf & "" _
& vbCrLf & "" _
& vbCrLf & " ndxTab = parseInt(ndxTab);" _
& vbCrLf & "" _
& vbCrLf & " var ndx, ndx0, bcolor," _
& vbCrLf & " last_tab, cur_tab," _
& vbCrLf & " last_frame, cur_frame;" _
& vbCrLf & " " _
& vbCrLf & " ndx =parseInt( ndxTab);" _
& vbCrLf & " ndx0 =parseInt( last_ndx);" _
& vbCrLf & " " _
& vbCrLf & " cur_tab =document.all(""onglet"" + ndx);" _
& vbCrLf & " last_tab =document.all(""onglet"" + ndx0);" _
& vbCrLf & " cur_table =document.all(""txt_Content"" + ndx);" _
& vbCrLf & " last_table =document.all(""txt_Content"" + ndx0);" _
& vbCrLf & " cur_frame =document.all(""nav_frame"" + ndx);" _
& vbCrLf & " last_frame =document.all(""nav_frame"" + ndx0);" _
& vbCrLf & " " _
& vbCrLf & " cur_tab.className =class_tab_selected;" _
& vbCrLf & " last_tab.className =class_tab;" _
& vbCrLf & "" _
& vbCrLf & " if( last_frame !=null)" _
& vbCrLf & " {" _
& vbCrLf & " last_frame.style.display =""none"";" _
& vbCrLf & " last_frame.style.visibility =""hidden"";" _
& vbCrLf & " }" _
& vbCrLf & " if( last_table !=null)" _
& vbCrLf & " {" _
& vbCrLf & " last_table.style.display =""none"";" _
& vbCrLf & " last_table.style.visibility =""hidden"";" _
& vbCrLf & " }" _
& vbCrLf & " if( cur_frame !=null)" _
& vbCrLf & " {" _
& vbCrLf & " cur_frame.style.display =""block"";" _
& vbCrLf & " cur_frame.style.visibility =""visible"";" _
& vbCrLf & " }" _
& vbCrLf & " if( cur_table !=null)" _
& vbCrLf & " {" _
& vbCrLf & " cur_table.style.display =""block"";" _
& vbCrLf & " cur_table.style.visibility =""visible"";" _
& vbCrLf & " }" _
& vbCrLf & " // sauvegarder l'inedx de l'onglet sélectioinné " _
& vbCrLf & " last_ndx =ndxTab;" _
& vbCrLf & "}")
response.Write(vbCrLf & "//-->" _
& vbCrLf & "</script>" _
& vbCrLf)
End Sub
Il faut maintenant écrire la méthode
output qui écrira effectivement les onglets dans l'objet
httpResponse :
'-----------------------------------------------------
'* méthode: écrire les onglets de la collection
'-----------------------------------------------------
Sub output (ByRef response As HttpResponse)
Dim n_tabs As Integer
Dim ndx As Integer
Dim cur_tab As html_onglet
Dim tag_table As New html_tag("table")
Dim tag_td As New html_tag("td")
'// écrire le script Java pour l'événement OnClick
output_toggle_tab_code(response)
'// écrire l'en-tête de la table contenant les onglets
tag_table.info = "id=""idTabs"" " _
& " width =""100%;"" " _
& " cellspacing=""0"" cellpadding=""0"""
response.Write(vbCrLf & "<!" & "-- les onglets -->")
tag_table.output_head(response)
response.Write(">" & vbCrLf & " <tr>" )
'// écrire les onglets
n_tabs = m_onglets.Count()
For ndx = 1 To n_tabs
cur_tab = m_onglets.Item(ndx)
cur_tab.output(response)
Next
'// écrire la fin de la table des onglets
response.Write(vbCrLf & "</tr>" _
& vbCrLf & "<tr>")
tag_td.info = " class=""nTabBottom"" colSpan=""" & n_tabs & """ " _
& " width=""100%;"""
tag_td.output_head(response)
response.Write("> ")
tag_td.output_foot(response)
response.Write(vbCrLf & "</tr>" & vbCrLf)
tag_table.output_foot(response)
'// écrire les iframes de chaque onglet
For ndx = 1 To n_tabs
cur_tab = m_onglets.Item( ndx)
cur_tab.output_doc_frame( response)
Next
End Sub
Figure 7 : Présentation des éléments de deux onglets
Tester notre travail
Créer les éléments complémentaires
La feuille de style
Pour pouvoir tester nos classes, il faut écrire les styles utilisés dans une feuille de styles.
Nous allons créer un fichier
onglets.css qui contiendra :
body
{
}
TD.nTab
{
BORDER-RIGHT: #6699cc 1px solid;
PADDING-RIGHT: 5px;
BORDER-TOP: #003366 2px solid;
PADDING-LEFT: 5px;
FONT-SIZE: 85%;
PADDING-BOTTOM: 2px;
BORDER-LEFT: #6699cc 1px solid;
CURSOR: hand;
COLOR: gainsboro;
TEXT-INDENT: 5px;
PADDING-TOP: 1px;
BORDER-BOTTOM: #99ccff 2px inset;
FONT-FAMILY: Tahoma, Verdana, Arial;
BACKGROUND-COLOR: indigo
}
TD.nTabSelected
{
BORDER-RIGHT: #99ccff 2px outset;
PADDING-RIGHT: 5px;
BORDER-TOP: #99ccff 2px outset;
PADDING-LEFT: 5px;
FONT-SIZE: 95%;
PADDING-BOTTOM: 2px;
BORDER-LEFT: #99ccff 2px outset;
CURSOR: default;
COLOR: ivory;
PADDING-TOP: 1px;
FONT-FAMILY: Tahoma, Verdana, Arial;
BACKGROUND-COLOR: cornflowerblue
}
TD.nTabBottom
{
border-right: #99ccff 2px outset;
font-size: 4%;
border-left: #99ccff 2px outset;
cursor: default;
font-family: Tahoma, Verdana, Arial;
height: 5px;
background-color: cornflowerblue;
}
La page destination (.asp !)
Nous allons aussi créer une page
contenu_onglet.asp qui affichera un contenu symbolique de chaque onglet.
La page aura les informations d'en-tête classiques, son corps ressemblera à ceci :
<p>
Ceci est le contenu de l'onglet : <b><%=Request.QueryString("tab")%></b>
</p>
Le formulaire de test
Il nous faut maintenant créer la page principale de test :
test_onglets.aspx.
C'est un formulaire web .NET (
Web Form) dont le corps ressemblera à ceci :
<body MS_POSITIONING="FlowLayout">
<%affiche_les_onglets()%>
</body>
affiche_les_onglets sera une
méthode de la classe de notre page de test (
test_onglets.aspx)
La méthode aura pour rôle de créer un objet de la classe onglets, ajouter 3 onglets ayant comme contenu la page
contenu_onglet.asp ouverte avec le numéro de l'onglet comme paramètre.
La méthode affichera ensuite les onglets par appel à la méthode output de la classe
onglets.
Pour ajouter cette méthode, nous allons ouvrir la page de code associée au formulaire web
test_onglets.aspx : dans VStudio.NET, cliquer le bouton droit sur la page formulaire et prendre Afficher le code.
'---------------------------------------------------
' tester les classes et traitement des onglets
'---------------------------------------------------
Sub affiche_les_onglets()
Dim m_onglets As New onglets()
m_onglets.add_tab( 0, "Onglet 1", "contenu_onglet.asp?tab=1")
m_onglets.add_tab( 1, "Onglet 2", "contenu_onglet.asp?tab=2")
m_onglets.add_tab( 2, "Onglet 3", "contenu_onglet.asp?tab=3")
m_onglets.output(Response)
m_onglets = Nothing
End Sub
Il suffit maintenant de compiler le projet, puis ouvrir le formulaire web
test_onglets.aspx.
Figure 8 : sortie test_onglets.aspx - onglet 1 actif
Figure 9: sortie test_onglets.aspx - onglet 2 actif
Non… ce n'est pas fini… tout ceci reste à optimiser…
C'est le début du long chemin habituel… bon courage !
- Dans un contexte assez anarchique dans le domaine de développement web, Microsoft lance .NET Framework : nouvelle plateforme de développement d'applications.
- ASP.NET, le module web de la plateforme .NET, n'est pas simplement un nouvel outil de développement… Il s'agit en effet d'une nouvelle approche, plus performante et, surtout, plus mature en matière de développement d'applications pour le web.
- Les différences entre ASP.NET et les outils 'traditionnels', dont ASP, sont des divergences de fond qui donnent un très grand avantage à ASP.NET.
- Ces divergences font de la migration d'une application web 'traditionnelle' vers ASP.NET un virage assez sérieux dans la vie d'un projet.
- La migration vers ASP.NET est en même temps une tâche gérable, car : « bien que [ASP.NET] soit conçu pour le software du futur, il supporte bien celui d'aujourd'hui et d'hier » !
L'exemple pratique exposé dans cet article démontre certains aspects de cette réalité.