Création d'un custom WebControl

Souvent utilisé, parfois mal utilisé ... Voici un petit post de rappel sur "comment créer un custom WebControl" basique composé de deux textbox sans passer par un CompositeControl. Notre cas concret ici sera donc la création d'un contrôle Identité. Bonne lecture ...

1) Page ASP.NET

Voici une page ASP.NET que l'on va écrire. Le contrôle est réutilisable dans n'importe quelle page. Nous avons enregistré l'assembly/Namespace dans le fichier web.config afin de pouvoir y accéder dans les pages. Pour cette démonstration, nous avons mis "demo". Cet exemple représente donc un contrôle avec un login / pass et un évènement informant quand l'utilisateur a changé.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Exemple controle bas niveau</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <demo:ControleIdentite ID="monControle" runat="server" />
        <asp:Button ID="btnSoumettre" runat="server" Text="Soumettre" />
    </div>
    </form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        monControle.IdentiteChanged += new EventHandler(monControle_IdentiteChanged);
    }
 
    void monControle_IdentiteChanged(object sender, EventArgs e)
    {
    }
}

2) Création du contrôle

a) Création de la classe.

Celle-ci représente donc les 2 contrôles. Chaque contrôle a un identifiant unique.

[Serializable]
public class Identite
{
    public string Prenom { get; set; }
    public string Nom { get; set; }
}
public class ControleIdentite : WebControl, INamingContainer
{
    public Identite Identite
    {
        get
        {
            object o = ViewState["Identite"];
            return (o == null) ? new Identite() : (Identite)o;
        }
 
        set
        {
            ViewState["Identite"] = value;
        }
    }
 
    public ControleIdentite()
        : base(HtmlTextWriterTag.Div) { }
 
    protected override void RenderContents(HtmlTextWriter writer)
    {
        base.RenderContents(writer);
        writer.Write("Nom : ");
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
        writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + this.ClientIDSeparator + "nom");
        writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID + this.IdSeparator + "nom");
        if (!string.IsNullOrEmpty(this.Identite.Nom))
            writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Identite.Nom);
 
        writer.RenderBeginTag(HtmlTextWriterTag.Input);
        writer.RenderEndTag();
        writer.WriteBreak();
        writer.Write("Prénom : ");
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
        writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + this.ClientIDSeparator + "prenom");
        writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID + this.IdSeparator + "prenom");
        if (!string.IsNullOrEmpty(this.Identite.Prenom))
            writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Identite.Prenom);
 
        writer.RenderBeginTag(HtmlTextWriterTag.Input);
        writer.RenderEndTag();
    }
}

b) Gestion du PostBack.

Si l'on teste l'exemple ci-dessus, on remarque rapidement que lorsqu'on clique sur le bouton de la page, on perd les données du contrôle. Nous allons donc ajouter la gestion du postback dans le contrôle ainsi que lever un évènement lorsque l'identité à changé. Pour ce faire, on va implémenter l'interface IPostBackDataHandler sur notre contrôle.

public class ControleIdentite : WebControl, INamingContainer, IPostBackDataHandler
{
    public event EventHandler IdentiteChanged;
 
    ....
 
    protected override void OnPreRender(EventArgs e)
    {
        // Il est important d'enregistrer le contrôle sur la page pour la gestion du PostBack
        Page.RegisterRequiresPostBack(this);
        base.OnPreRender(e);
    }
 
    #region IPostBackDataHandler Members
 
    bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
    {
        // On va regarder si l'identité à changée. Si on retourne true, la méthode RaisePostDataChangedEvent
        // sera lancée. 
        Identite identite = this.Identite;
        var nom_value = postCollection[this.UniqueID + this.IdSeparator + "nom"];
        var prenom_value = postCollection[this.UniqueID + this.IdSeparator + "prenom"];
        bool hasChanged = false;
        if (identite.Nom != nom_value)
        {
            identite.Nom = nom_value;
            hasChanged = true;
        }
 
        if (identite.Prenom != prenom_value)
        {
            identite.Prenom = prenom_value;
            hasChanged = true;
        }
        if (hasChanged)
            this.Identite = identite;
 
        return hasChanged;
    }
 
    void IPostBackDataHandler.RaisePostDataChangedEvent()
    {
        // Nous levons l'évènement de changement de l'identité
        if (IdentiteChanged != null)
            IdentiteChanged(this, EventArgs.Empty);
    }
 
    #endregion
}

3) Conclusion

Vous venez de voir comment créer un petit contrôle de bas niveau gérant le postback sans passer par un contrôle Composite. Ce type de contrôle est certes souvent plus verbeux mais on maîtrise exactement l'html généré ainsi que tout ce qui est mis dans le ViewState. Si vous avez la moindre question, n'hésitez pas à me l'envoyer par mail ou en commentaire ici.

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

Fil des commentaires de ce billet

Page top