Un peu de bulle ? Où comment faire remonter un événement avec OnBubbleEvent - RaiseBubbleEvent
Lorsque l'on met un LinkButton dans un Repeater, comment ce fait-il que si l'on clique sur le linkbutton alors l'événement ItemCommand du Repeater est declenché ?
<script type="text/C#" runat="server">
protected void Page_Load(object sender, EventArgs e)
{
rptTest.DataSource = new int[] { 1, 2, 3 };
rptTest.DataBind();
}
protected void rptTest_ItemCommand(object source, RepeaterCommandEventArgs e)
{
lblTest.Text = DateTime.Now.ToLongTimeString();
}
</script>
<asp:Repeater runat="server" ID="rptTest" OnItemCommand="rptTest_ItemCommand">
<ItemTemplate>
<asp:LinkButton runat="server" ID="lbTest">Click Me !</asp:LinkButton>
</ItemTemplate>
</asp:Repeater>
<asp:Label runat="server" ID="lblTest" />
Il s'agit bien de l'événement ItemCommand du Repeater qui est declenché, pourquoi ?
Si l'on ne connait pas le fonctionnement d'un postback cela peut vous paraitre naturel. Voici une explication rapide du fonctionnement d'un postback. Regardons tout d'abord le code HTML généré par l'exemple plus haut, on voit que le LinkButton s'est transformé en :
<a id="rptTest_ctl00_lbTest" href="javascript:__doPostBack('rptTest$ctl00$lbTest','')">Click Me !</a>
<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
//]]>
</script>
Lorsque l'on on clique sur le lien, on appelle la méthode __doPostBack qui va renseigner le champ caché __EVENTTARGET par le UniqueID du LinkButton, puis va envoyer le formulaire grâce à la méthode submit.
Côté serveur, le cycle de vie de la page se poursuit classiquement, Init, Load, ... Lorsque tous les contrôles sont recréés c'est au tour de la méthode RaisePostBackEvent d'intervenir. Cette méthode récupère le contrôle source en faisant un FindControl avec Request.Form["__EVENTTARGET"]. Si ce contrôle est trouvé et qu'il implémente IPostBackEventHandler alors la méthode RaisePostBackEvent de celui-ci est appelée avec en paramètre le contenu de Request.Form["__EVENTARGUMENT"]. C'est pour cela que si vous faîtes un contrôle faisant un PostBack, il faut implémenter IPostBackEventHandler.
public interface IPostBackEventHandler
{
void RaisePostBackEvent(string eventArgument);
}
Dans Notre cas la méthode RaisePostBackEvent du LinkButton est directement appellé, le Repeater ne peut pas l'intercepter, alors comment déclenche t'il son événement ItemCommand ?
En fait, ASP.net permet de remonter un événement. Pour cela il faut appeler la méthode RaiseBubbleEvent dans la méthode RaisePostBackEvent.
C'est justement ce que je viens de rajouter à mon PostBackControl :
public void RaisePostBackEvent(string eventArgument)
{
CommandEventArgs e = null;
// ...
if (CallBack != null)
{
CallBack.Invoke(this, e);
}
base.RaiseBubbleEvent(this, e);
}
Nous avons donc remonté l'événement dans l'arbre de contrôle. Pour que le Repeater intercepte cet événement il lui faut surcharger la méthode OnBubbleEvent qui renvoie vrai si l'on veut arrêter la propagation.
Voici un exemple :
protected override bool OnBubbleEvent(object source, EventArgs e)
{
if (e is NewsListEventArgs)
{
NewsListEventArgs args = (NewsListEventArgs)e;
if (args.CommandName.Equals("Activate"))
{
// On séléctionne le nouvel SelectedDataKey
this.ActiveDatakey = GetDataKey(args.Item.ItemIndex);
OnActiveIndexChanged(args);
return true;
}
}
return false;
}
Cette astuce s'avère très pratique lorsque l'on fait des contrôles pérsonalisés très riche.