Maîtriser les événements ASP.NET - Partie 4/5
Auteur : Frédéric De Lène Mirouze (amethyste16@hotmail.com)
8 - Architecture des événements ASP côté serveur
Les événements sans données de publication
Cas où le composant doit générer le javascript d'appel de l'événement
Cas où il y a plusieurs événements
Les événements avec données de publication
Cas d'un composant avec un seul contrôle
Cas d'un composant avec plusieurs contrôles
Percolation d'un événement à travers la hiérarchie des composants
9 - Les événements asynchrones
Nous avons vu comment ASP lève un événement du point de vue de la page HTML. Mais que se passe t'il côté serveur?
ASP s'appuie sur deux interfaces: IPostBackEventHandler et IPostBackDataHandler. Dès qu'un contrôle implémente l'une d'entre elle ou bien les deux, celui-ci est automatiquement inséré dans la chaîne de gestion des événements ASP. Il n'y a rien de plus à faire.
Il y a deux interfaces parce que l'on distingue deux types d'événement qui vont chacun correspondre à une plomberie spécifique:
Voyons les détails ensembles.
D'abord examinons notre
interface IPostBackEventHandler
. Elle ne déclare qu'une seule méthode: RaisePostBackEvent.
Cette méthode est automatiquement appelée par ASP pour demander au composant
d'exécuter la logique qui lève ou non ses événements.
Il appartient bien sûr au concepteur du composant de l'implémenter.
Nous allons développer un composant NewDdl qui hérite de DropDownList, mais implémente un nouvel événement. Cet événement se déclenche chaque fois que l'on sélectionne un article d'indice supérieur à 10 dans la liste.
Listing 8-1: Premier composant
Code C#
public sealed class NewDdl : DropDownList,IPostBackEventHandler
{public NewDdl()
{ } public event EventHandler ValeurElevee;
protected void OnValeurElevee(EventArgs e)
{if (ValeurElevee != null)
{ValeurElevee(this, e);
} } void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{if (this.SelectedIndex > 10)
{ OnValeurElevee(EventArgs.Empty); } } protected override bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{this.Page.RegisterRequiresRaiseEvent(this);
return base.LoadPostData(postDataKey, postCollection);
} protected override void OnPreRender(EventArgs e)
{base.OnPreRender(e);
this.Page.RegisterRequiresRaiseEvent(this);
} protected override void Render(HtmlTextWriter writer)
{
if (this.Page != null)
{// indispensable pour que les propriétés du contrôle soient postées
this.Page.VerifyRenderingInServerForm(this);
}base.Render(writer);
}}
Code VB
Public NotInheritable Class NewDdl
Inherits DropDownList
Implements IPostBackEventHandler
Public Sub New()
End Sub
Public Event ValeurElevee As EventHandler
Protected Sub OnValeurElevee(ByVal e As EventArgs)
RaiseEvent ValeurElevee(Me, e)
End Sub
Sub IPostBackEventHandler.RaisePostBackEvent(ByVal eventArgument As String)
If Me.SelectedIndex > 10 Then
OnValeurElevee(EventArgs.Empty) End If
End Sub
Protected Overloads Overrides Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean
Me.Page.RegisterRequiresRaiseEvent(Me)
Return MyBase.LoadPostData(postDataKey, postCollection)
End Function
Protected Overloads Overrides Sub OnPreRender(ByVal e As EventArgs)
MyBase.OnPreRender(e)
Me.Page.RegisterRequiresRaiseEvent(Me)
End Sub
Protected Overloads Overrides Sub Render(ByVal writer As HtmlTextWriter)
If Not (Me.Page Is Nothing) Then
' indispensable pour que les propriétés du contrôle soient postées
Me.Page.VerifyRenderingInServerForm(Me)
End If
MyBase.Render(writer)
End Sub
End Class
On commence par implémenter un événement ValeurElevee selon le modèle standard avec la méthode On (voir l'annexe dans la première partie du tutoriel).
Le contrôle implémente IPostBackEventHandler, ASP va donc appeler RaisePostBack à un certain moment du cycle de vie de la page, nous verrons quand dans la dernière partie du tutoriel. Le code de cette méthode est simple, il exécute un test pour savoir s'il doit lever ou non l'événement. Dans ce cas il appelle la méthode OnValeurElevee.
Note:
Les appels à RegisterRequiresRaiseEvent
sont rendus nécessaires parce que le composant hérite d'un contrôle qui implémente
IPostBackDataHandler. Sans cet
appel RaisePostBack n'est pas
appelé, nous verrons pourquoi plus loin. Si IPostBackDataHandler
n'est pas implémenté, l'appel à RaisePostBack
nest pas nécessaire comme nous le constaterons aussi avec les deux exemples qui
suivent.
Ne me dites pas que c'est difficile!
Examinons un autre cas de figure, le composant NewLabel:
Listing 8-2: Composant NewLabel
Code C#
public class NewLabel : WebControl, IPostBackEventHandler
{public NewLabel()
{ } public string Text
{ get {if (this.ViewState["Text"] == null)
{return "Hello le monde";
}return this.ViewState["Text"] as string;
} set {this.ViewState["Text"] = value;
} } public event EventHandler Click;
protected void OnClick(EventArgs e)
{if (Click != null)
{Click(this,e);
} } public void RaisePostBackEvent(string eventArgument)
{this.OnClick(EventArgs.Empty);
} protected override void RenderContents(HtmlTextWriter writer)
{writer.Write(this.Text);
} protected override void Render(HtmlTextWriter writer)
{
if (this.Page != null)
{// indispensable pour que les propriétés du contrôle soient postées
this.Page.VerifyRenderingInServerForm(this);
}base.Render(writer);
}}
Code VB
Public Class NewLabel
Inherits WebControl
Implements IPostBackEventHandler
Public Sub New()
End Sub
Public Property Text() As String
Get
If Me.ViewState("Text") Is Nothing Then
Return "Hello le monde"
End If
Return CType(ConversionHelpers.AsWorkaround(Me.ViewState("Text"), GetType(String)), String)
End Get
Set
Me.ViewState("Text") = value
End Set
End Property
Public Event Click As EventHandler
Protected Sub OnClick(ByVal e As EventArgs)
RaiseEvent Click(Me, e)
End Sub
Sub IPostBackEventHandler.RaisePostBackEvent(ByVal eventArgument As String)
Me.OnClick(EventArgs.Empty)
End Sub
Protected Overloads Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
writer.Write(Me.Text)
End Sub
Protected Overloads Overrides Sub Render(ByVal writer As HtmlTextWriter)
If Not (Me.Page Is Nothing) Then
Me.Page.VerifyRenderingInServerForm(Me)
End If
MyBase.Render(writer)
End Sub
End Class
Le composant fonctionne correctement, il expose un événement Click auquel on peut s'abonner. Malheureusement rien n'est prévu pour le déclencher bien que nous ayons implémenté IPostBackEventHandler.
La raison est qu'il manque côté client le javascript nécessaire. Dans l'exemple précédent nous héritions de DropDownList et récupérions son événement standard SelectedIndexChanged. L'ajout du script client était donc déjà implémenté.
Dans notre cas il nous appartient de le faire. Nous avons vu au chapitre 7 comment s'y prendre, il n'y a rien de nouveau ici.
Listing 8-3: Ajout du déclenchement des événements
Code C#
protected override void AddAttributesToRender(HtmlTextWriter writer)
{base.AddAttributesToRender(writer);
string DoPostBack =
this.Page.ClientScript.GetPostBackEventReference(new PostBackOptions(this));
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, DoPostBack);}
Code VB
Protected Overloads Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
MyBase.AddAttributesToRender(writer)
Dim DoPostBack As String =
Me.Page.ClientScript.GetPostBackEventReference(New PostBackOptions(Me))
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, DoPostBack) End Sub
Nous disposons donc d'un contrôle de type Label qui lève un événement Click lorsque l'utilisateur clique dessus. Je ne vais pas jusqu'à dire que c'est le genre de comportement qu'il est normal de développer, mais je voulais un exemple à partir d'un contrôle que l'on ne puisse soupçonner d'être pollué par une plomberie pré existante.
Notez également que contrairement à l'exemple précédent, nous n'avons nul besoin de faire un appel à RegisterRequiresRaiseEvent.
Notre nouveau composant affiche une valeur numérique et en dessous deux labels Plus et Moins qui incrémentent ou décrémentent la valeur numérique.
![]()
Nous souhaitons disposer de deux événements: un pour chacune des actions. Tant que l'on y est on passera dans l'événement la valeur affichée.
Listing 8-4: Composant Compteur
Code C#
public class Compteur : WebControl, IPostBackEventHandler
{public Compteur()
{ } public int Total
{get
{if (this.ViewState["Total"] == null)
{return 0;
}return Convert.ToInt32(this.ViewState["Total"]);
} set {this.ViewState["Total"] = value;
} } public delegate void CompteurEventHandler(object sender, CompteurEventArgs e);
public event CompteurEventHandler Plus;
protected void OnPlus(CompteurEventArgs e)
{if (Plus != null)
{
Plus(this, e);
} } public event CompteurEventHandler Moins;
protected void OnMoins(CompteurEventArgs e)
{if (Moins != null)
{Moins(this, e);
} } void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
if (eventArgument == "plus")
{this.Total++;
this.OnPlus(new CompteurEventArgs(this.Total));
}else
{this.Total--;
this.OnMoins(new CompteurEventArgs(this.Total));
} } protected override void RenderContents(HtmlTextWriter writer)
{writer.Write(this.Total);
writer.WriteBreak(); // <br/>
string DoPostBack =
this.Page.ClientScript.GetPostBackEventReference(this, "moins");
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, DoPostBack); writer.RenderBeginTag(HtmlTextWriterTag.Span);writer.Write("Moins");
writer.RenderEndTag(); writer.Write(" ");
DoPostBack = this.Page.ClientScript.GetPostBackEventReference(this, "plus");
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, DoPostBack); writer.RenderBeginTag(HtmlTextWriterTag.Span);writer.Write("Plus");
writer.RenderEndTag(); } protected override void Render(HtmlTextWriter writer)
{
if (this.Page != null)
{// indispensable pour que les propriétés du contrôle soient postées
this.Page.VerifyRenderingInServerForm(this);
}base.Render(writer);
}
} public class CompteurEventArgs : EventArgs
{public CompteurEventArgs()
{ } public CompteurEventArgs(int total)
{this.Total = total;
} private int _Total;
public int Total
{ get {return _Total;
} set {_Total = value;
} }}
Code VB
Public Class Compteur
Inherits WebControl
Implements IPostBackEventHandler
Public Sub New()
End Sub
Public Property Total() As Integer
Get
If Me.ViewState("Total") Is Nothing Then
Return 0
End If
Return Convert.ToInt32(Me.ViewState("Total"))
End Get
Set
Me.ViewState("Total") = value
End Set
End Property
Public Delegate Sub CompteurEventHandler(ByVal sender As Object, ByVal e As CompteurEventArgs)
Public Event Plus As CompteurEventHandler
Protected Sub OnPlus(ByVal e As CompteurEventArgs)
RaiseEvent Plus(Me, e)
End Sub
Public Event Moins As CompteurEventHandler
Protected Sub OnMoins(ByVal e As CompteurEventArgs)
RaiseEvent Moins(Me, e)
End Sub
Sub IPostBackEventHandler.RaisePostBackEvent(ByVal eventArgument As String)
If eventArgument = "plus" Then
System.Math.Min(System.Threading.Interlocked.Increment(Me.Total),Me.Total-1)
Me.OnPlus(New CompteurEventArgs(Me.Total))
Else
System.Math.Max(System.Threading.Interlocked.Decrement(Me.Total),Me.Total+1)
Me.OnMoins(New CompteurEventArgs(Me.Total))
End If
End Sub
Protected Overloads Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
writer.Write(Me.Total)
writer.WriteBreak ' <br/>
Dim DoPostBack As String = Me.Page.ClientScript.GetPostBackEventReference(Me, "moins")
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, DoPostBack) writer.RenderBeginTag(HtmlTextWriterTag.Span) writer.Write("Moins")
writer.RenderEndTag writer.Write(" ")
DoPostBack = Me.Page.ClientScript.GetPostBackEventReference(Me, "plus")
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, DoPostBack) writer.RenderBeginTag(HtmlTextWriterTag.Span) writer.Write("Plus")
writer.RenderEndTag End Sub
Protected Overloads Overrides Sub Render(ByVal writer As HtmlTextWriter)
If Not (Me.Page Is Nothing) Then
Me.Page.VerifyRenderingInServerForm(Me)
End If
MyBase.Render(writer)
End Sub
End Class
Public Class CompteurEventArgs
Inherits EventArgs
Public Sub New()
End Sub
Public Sub New(ByVal