10 -Ordonnancement des événements
les étapes du chargement d'une page
Retour sur le chargement dynamique des contrôles
Les événements des pages maîtres
Note:
Il est important pour lire ce chapitre de bien
comprendre le fonctionnement du viewstate, mais aussi de
l'architecture d'ASP. Au besoin se référer à la
bibliographie [29,30,31] ou aux parties précédentes de
ce tutoriel.
L'ordonnancement des événements désigne la séquence des événements joués lorsqu'une page est chargée.
Nous allons retracer ces étapes, mais également positionner l'instant où certaines méthodes clefs sont appelées. Une chose à avoir en tête est que quel que soit le contexte (page simple, master page...) et que l'on fasse où non un renvoi, l'ordonnancement des événements est toujours le même. Tout au plus certains événements vont ou non se déclencher.
Globalement le cycle de vie ASP 2.0 est identique à celui d'ASP 1.X, par contre la granulométrie est considérablement plus fine.
Même si je ne vais pas le mentionner par la suite, notez aussi que dans le cas des événements, la méthode On associée à l'événement est toujours exécutée avant un éventuel gestionnaire d'événement (OnLoad avant Page_Load). C'est évident me direz vous puisque l'un appelle l'autre, mais parfois cela va mieux en le disant!
Il n'y a pas de différence de fond entre surcharger une méthode On ou créer un gestionnaire d'événement. Simplement dans le gestionnaire de page vous n'avez pas besoin d'appeler la méthode On de la classe de base.
Note:
J'ai mis en bibliographie des
articles, éventuellement MSDN, évoquant l'ordonnancement
des événements. Vous constaterez des divergences avec
mon propre article. Que se passe t'il au juste? La
plupart du temps ces articles datent des versions béta.
Je pense que les informations trouvées dans cet article sont à jour parce que je suis allé les vérifier (merci Reflector et Trace!!!). Mais rien ne prouve que je ne suis pas passé à côté de quelquechose.
Note:
J'ai rencontré une difficulté pratique en rédigeant
ce chapitre. Lorsqu'une page se charge, un certain nombre
d'événements sont lancés. Mais ce n'est pas la totalité
de l'histoire car des méthodes sont également exécutées
et elles participent pleinement à la compréhension du
cycle de vie, en particulier pourquoi peut t'on faire
telle action lors de cet événement et pas l'autre?
Par exemple il semble impensable de ne pas mentionner à quel moment TrackViewState est appelé. Mais alors où placer la limite? Dans un premier temps je pensais me limiter uniquement au méthodes surchargeables, mais en examinant le source de la classe Page avec Reflector je me suis rendu compte que certaines méthodes private auraient une place légitime dans le graphe tandis que certaines méthodes surchargeables un intérêt limité.
Alors j'ai fais des choix. J'ai ajouté des méthodes et des événements et supprimés d'autres au risque de parfois paraître un peu arbitraire.
Si vous souhaitez quelque chose de plus complet, voyez ici[48].
Les étapes du chargement d'une page sont les suivantes [25]:
| Etape | Description |
| Requête | ASP détermine si le fichier aspx a besoin d'être analysé et compilé (le cycle de vie de la page démarre alors) ou bien si une version existe en cache et doit être renvoyée comme réponse à la requête. |
| Démarrage | Le cycle de vie de la page
démarre. En particulier le constructeur de la
page est appelé. Les propriétés de la page telles Request et Response sont chargées. La page détermine également s'il s'agit d'une nouvelle requête ou bien d'un renvoi et charge en conséquence IsPostBack. Les propriétés de culture sont également déterminées. |
| Initialisation | Durant cette phase, les contrôles de la page sont disponibles et la propriété UniqueID de ces contrôles est calculée. Le thème en cours est également appliqué à la page. |
| Chargement | Si la requête est un renvoi,
les donnés de publication renvoyées dans le paquet HTTP sont
chargées et les propriétés des contrôles sont
restaurées à leur valeur viewstate et control
state. Note: ControlState est une nouveauté d'ASP 2.0 qui en gros implémente un viewstate dont la portée est le contrôle. J'en parle plus longuement dans le tutoriel consacré au viewstate. |
| Validation |
La méthode Validate de tous les contrôles validateur est appelée. La propriété IsValid des contrôles et de la page est chargée. |
| Evénements de renvoi | Si la requête est un renvoi, alors les événements de renvoi sont levés. |
| Rendu | Le viewstate est sauvegardé. La page appelle la méthode Render de chaque contrôle qui alimente le flux Response.OutputStream. |
| Déchargement | La page est entièrement rendu. Elle est alors renvoyée vers le poste client et côté serveur l'objet Page devient prêt à être déchargé. |
Lors de l'étude du viewstate il a été dit que lorsqu'un contrôle est ajouté dynamiquement, ASP rejoue la séquence des événements de ce contrôle pour que son état soit synchronisé avec celui de son parent. En particulier les événements de chargement du viewstate sont rejoués. On appellera rattrapage ce mécanisme. Dans son (excellent) livre, Nikhil Kothari utilise le terme de catch-up[4].
L'avantage de ce mécanisme est que l'on peut ajouter un contrôle dynamiquement presque à tout moment du cycle de vie de la page. Le rattrapage est effectué dans la méthode Control.AddControl[52].
Dans la mesure où le développeur peut choisir d'ajouter le contrôle à tout instant, la liste des événements à rattraper peut varier. Il est donc clair que le rattrapage doit être piloté par un état interne relatif au composant propriétaire de la collection où le contrôle est ajouté afin de savoir déterminer les étapes manquantes.
Cet état est définit par la propriété ControlState qui prend ses valeurs dans l'énumération ControlState. Cette énumération s'est enrichie avec l'arrivée d'ASP 2.0.
A l'exception de Constructed qui est une étape où rien n'est encore initialisé, chaque étape correspond avec le lancement d'une où plusieurs méthodes dont le nom est celui de l'événement correspondant suivit du terme Recursive. Par exemple LoadRecursive lancé si le contrôle parent a déjà été chargé.
Le tutoriel est accompagné de deux posters représentant l'ordonnancement des événements en ASP. L'un en couleur, l'autre en noir et blanc.
Nous reproduisons ici la version NB avant de la commenter un peu plus loin:

| Constructeur | Certains se demandent pourquoi utiliser
Init plutôt que le constructeur comme on le
fait pour les autres classes. La réponse se
trouve dans le diagramme. Entre le constructeur et l'événement Init il y a pas mal de monde! On a probablement peu de choses bien utile de faisable dans le constructeur. Les contrôles ne sont pas chargés, on ne connaît pas le mode de la requête... |
| Construct | Exécution d'une logique au moment du design.
La méthode est appelée depuis le constructeur de
TemplateControl (la classe de base de
page). Elle est donc en réalité exécutée avant
le constructeur de la page en cours. Pour être honnête, j'ignore à quoi ça sert! |
| FrameworkInitialize | Inauguration de l'initialisation
de la page en créant la hiérarchie des contrôles,
initialisant les styles... On n'a peu de raisons de se soucier de cette méthode. |
| InitializeCulture | Comme l'indique son nom. Permet
d'implémenter la logique qui va associer la
culture du site au thread en cours (Culture
et UICulture). Si vous surchargez cette méthode, créez pour cela une classe qui hérite de Page et faites hériter les pages du site de cette classe. |
| AddParsedSubObject AddedControl |
Création de la hiérarchie des contrôles de
la page. Ces méthodes sont appelées en boucle
autant de fois qu'il y a de contrôles dans la
page. En général, mais pas toujours, on a donc 3 appels (deux pour un LiteralControl et un pour un HtmlForm). Chaque composant lance des appels similaires pour ses contrôles enfants. Bien sûr la présence de contrôles serveur (head par exemple) de premier niveau peut modifier le nombre d'appels effectués. Lors du premier appel est également lancé CreateControlCollection. AddParsedSubObject est une méthode déclarée dans IParserAccessor que Control implémente. Elle est automatiquement appelée par le ControlBuilder (classe qui implémente l'analyseur de page et génère un contrôle et ses enfants) du contrôle chaque fois qu'un élément ASP, XML ou HTML de la page a été analysé et transformé en objet afin de l'ajouter à la collection de contrôle. On peut surcharger cette méthode si par exemple on souhaite récupérer la liste des contrôles d'un type donné dans une table afin d'en disposer sans avoir à reparcourir toute la collection de contrôle. AddedControl est appelée après qu'une contrôle enfant a été ajouté à la collection des contrôles. Cette méthode supprime le contrôle d'un parent éventuel, puis initialiser l'ID du contrôle à l'aide de Control.GenerateAutomaticID, et enfin lance le mécanisme de rattrapage détaillé dans le chapitre précédent. |
| DeterminePostbackMode | Charge bien sûr IsPostBack, mais récupère aussi la collection des données publiées dans la page en POST et GET. |
| PreInit |
La hiérarchie des contrôles déclarés dans
aspx est construite et les données postées sont
connues.
On remarque au passage que le thème est
appliqué avant la page maître. C'est ce qui
explique que les pages maîtres ne supportent pas
les thèmes. Il existe toutefois divers
contournements [54,55]. |
| Init | Tous les contrôles enfants ont été
initialisés, les propriétés de skin et les
thèmes chargés
également. C'est le bon endroit pour initialiser les propriétés de la page car c'est après cet événement que TrackViewState est appelé. Notez que l'événement Init et l'appel à TrackViewState des contrôles enfants se produit avant celui du contrôle parent. |
| TrackViewState | Active le suivi des propriétés |
| InitComplete | L'initialisation est terminée. A ce stade les informations saisies par l'utilisateur n'ont pas encore été chargées dans les contrôles. |
| LoadPageStateFromPersistentMedium LoadControlState LoadViewState LoadPostdata |
A ce stade on alimente les contrôles avec
les saisies de l'utilisateur. LoadPageStateFromPersistentMedium est appelée pour charger le viewstate depuis son conteneur. En général c'est la page, mais pas nécessairement. LoadControlState et LoadViewState sont très similaires, le ControlState est une variante du viewstate pour les contrôles. Les propriété marquées comme dirty sont rechargées par LoadViewState et donc marquées à nouveau comme dirty puisque TrackViewState a été appelé précédemment. La méthode inverse est bien sûr SaveViewState qui est lancé plus loin. LoadPostData recharge les saisies de l'utilisateur. |
| PreLoad | Premier événement où les contrôles sont entièrement initialisés, mais avant l'événement Load. |
| Load | L'événement Load de la page est
appelé. Les événements Load des composants sont ensuite récursivement appelés. Comme le montre le diagramme les événements de la page (IPostBackDataHandler ou IPostBackEventHandler) sont appelés avant ceux des contrôles enfants. Les événements DataHandler (CheckBox.CheckedChanged, DropDownList.SelectedIndexChanged...) sont appelés avant les événements EventHandler (click sur un Button par exemple). Pour finir BubbleEvent est traité. C'est aussi lors de cet événement que l'on appelle la méthode DataBindind de chaque contrôle. C'est aussi le meilleurs endroit pour charger les données, les événements suivants ayant plutôt un lien avec le rendu. Bien évidemment cette analyse peut parfois être nuancée. |
| LoadComplete | Tous les traitements relatifs au chargement des états la page sont terminés. La page est alors prête à être rendue. |
| PreRender | La page appelle EnsureChildControls
pour chaque contrôle enfant. L'événement PreRender est ensuite levé. On peut alors faire les dernières modifications sur les contrôles avant leur rendu sur la page comme fixer leur propriété Visible. |
| PreRenderComplete | Dernier événement déclenché avant le rendu
des contrôles sur la page. Les contrôles sont chargés, initialisés, les paginations faites. |
| SaveControlState SaveViewState |
Copie les propriétés marquées comme dirty dans le viewstate |
| SavePageStateToPersistentMedium | Le viewstate est sérialisé |
| SaveStateComplete | La page a terminé l'enregistrement de toutes ses informations d'état. A partir de là, tous les changements sur la page seront ignorés. |
| Render | Effectue le rendu de la page |
| UnLoad | Effectue les nettoyages finaux comme la
fermeture éventuelle d'une connexion à une base
de donnée (mouais...), log du chargement de la page... Toute tentative de modification de la page à ce stade lève une exception. |
