Créer un contrôle TextBox en C# / ASP.NET

Rémy Mainil

Cet article a pour but d'illustrer le processus de création d'un contrôle serveur ASP.NET écrit en C# au travers de la création d'un contrôle de type textbox.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Créer un contrôle TextBox en C# / ASP.NET

- Auteur : Rémy Mainil

- Parution : 24 février 2009

- Licence : Copyright ® 2009 Rémy Mainil. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Les bases

- II. Un contrôle personnalisable

- III. Le HTML sans erreur avec HtmlTextWriter

- IV. WebControl plutôt que Control

- V. Création d'un Textbox

- VI. PostBack automatique

- VII. Conclusion

- VIII. Remerciements

I - Les bases

La base de tout contrôle ASP.NET, qu'il s'agisse d'un contrôle serveur ou d'un contrôle utilisateur est la classe Control du namespace System.Web.UI. Créer un contrôle serveur revient donc simplement à créer une classe qui hérite de cette classe Control.
Entrons directement dans le vif du sujet et créons notre premier contrôle. Il suffit pour cela de saisir le code suivant :

Premier Contrôle ASP.NET

using System;
using System.Web;
using System.Web.UI;

namespace Dvp.Controls
{
public class MonPremierControl : Control
{
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<h1>Mon premier contrôle...</h1>");
}
}
}

Nous pouvons utiliser notre contrôle fraichement créé comme ceci :

Utilisation de notre premier contrôle

<%@ Register TagPrefix="MyCtl" Namespace="Dvp.Controls" Assembly="MonControle" %>
<html>
<body>
<MyCtl:MonPremierControle runat="server" />
</body>
</html>

Et voici le résultat :

Notre premier contrôle serveur.

La méthode Render que nous avons surchargée est une méthode qui sera invoquée pour tous les contrôles contenus dans une page et qui a pour objectif de publier le code HTML nécessaire pour la présentation du contrôle.

À travers ces quelques lignes de code, nous venons de créer en très peu de temps notre premier contrôle serveur.
Dans la section suivante, nous allons voir comment rendre ce contrôle « personnalisable ».

II - Un contrôle personnalisable

Notre contrôle, bien que très facile à créer, n'est pour le moment d'aucune utilité.
Pour le rendre plus intéressant, nous allons voir comment lui ajouter des propriétés afin de le rendre personnalisable pour qu'il puisse être adapté en fonction des besoins.
Nous allons commencer par lui ajouter une propriété Text qui va déterminer le texte affiché par le contrôle.
Pour cela, nous déclarons simplement une propriété dans notre contrôle et nous lui faisons afficher la valeur de cette propriété dans la méthode Render.

Ajout d'une propriété Text à notre contrôle

public class MonPremierControl : Control
{
private string _text = "Texte par défaut";
public string Text
{
get { return _text; }
set { _text = value; }
}

protected override void Render(HtmlTextWriter writer)
{
writer.Write("<h1>" + this._text + "</h1>");
}
}

Nous pouvons maintenant utiliser notre contrôle en lui passant le texte que nous souhaitons voir afficher. Nous procédons pour cela à l'ajout d'un attribut à la balise de notre contrôle.

Utilisation du contrôle personnalisable

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register TagPrefix="MyCtl" Namespace="Dvp.Controls" Assembly="MesControles" %>
<html>
<body>
<form runat="server">
<MyCtl:MonPremierControl runat="server" Text="Ce contrôle est personalisable !" />
</form>
</body>
</html>

Rendu HTML de notre contrôle personnalisé

Ajoutons maintenant des propriétés pour influencer le style du texte présenté par notre contrôle.
Nous allons ajouter une propriété FontSize pour décider de la taille de la police et Color pour déterminer la couleur du texte.
Voici à quoi correspond le code de notre contrôle :

Des propriétés de style pour notre contrôle

public class MonPremierControl : Control
{
private string _text = "Texte par défaut";
public string Text
{
get { return _text; }
set { _text = value; }
}

private Color _color = Color.Black;
public Color Color
{
get { return _color; }
set { _color = value; }
}

private int _fontSize = 18;
public int FontSize
{
get { return _fontSize; }
set { _fontSize = value; }
}

protected override void Render(HtmlTextWriter writer)
{
writer.Write("<h1 style=\"color:" + ColorTranslator.ToHtml(_color) + "; font-size:" + _fontSize + " px;\">" + this._text + "</h1>");
}
}

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register TagPrefix="MyCtl" Namespace="Dvp.Controls" Assembly="MesControles" %>

<html>
<body>
<form runat="server">
<MyCtl:MonPremierControl runat="server" Text="Ce contrôle est personnalisable !" FontSize="10" Color="Aqua" />
</form>
</body>
</html>

Le style de notre contrôle est maintenant personnalisable également.

Vous voyez qu'il est très simple, au travers des propriétés, de permettre de modifier le comportement de notre contrôle.
La « magie » d'ASP.NET est la conversion automatique des propriétés déclarées sous forme d'attributs dans les balises déclaratives des contrôles.
Ainsi, dans notre exemple, ASP.NET se contente d'effectuer un simple mapping pour la propriété Text vu que le type de la propriété est string, mais il effectue automatiquement les conversions nécessaires pour les propriétés FontSize et Color de type int et enum respectivement.

N'oubliez pas de donner des valeurs par défaut à vos propriétés afin que le contrôle soit utilisable sans devoir en préciser toutes les propriétés.

Nous avons maintenant un contrôle dont on peut influencer le contenu et le style. Dans la section suivante, nous allons voir comment améliorer la façon dont nous générons le code HTML de notre contrôle.

III - Le HTML sans erreur avec HtmlTextWriter

Nous n'avons pas encore ajouté beaucoup de propriétés à notre contrôle que déjà le code HTML à générer est devenu plus complexe.
Afin de nous faciliter la tâche et de réduire fortement le risque d'apparition d'erreurs dans le code HTML produit, nous allons utiliser la classe HtmlTextWriter pour construire le code HTML de notre objet.
Cette classe propose différentes méthodes permettant notamment :

- la gestion de la création de balises HTML bien formées ;

- la création d'attributs et d'attributs de style.

En utilisant les méthodes de la classe HtmlTextWriter, nous pouvons réécrire la méthode Render de notre contrôle comme ceci :

La méthode Render réécrite avec l'aide de HtmlTextWriter

protected override void Render(HtmlTextWriter writer)
{
writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, _fontSize.ToString() + "px;");
writer.AddStyleAttribute(HtmlTextWriterStyle.Color, ColorTranslator.ToHtml(_color));
writer.RenderBeginTag(HtmlTextWriterTag.H1);
writer.Write(this._text);
writer.RenderEndTag();
}

Le rendu du contrôle sera identique à celui obtenu précédemment, mais le code est ainsi beaucoup plus lisible et facile à comprendre et son écriture est moins susceptible de contenir des erreurs.
La méthode AddStyleAttribute agit selon le principe d'une pile. Les appels successifs à cette méthode « entassent » les propriétés de style définies pour les ajouter toutes ensemble à la première balise HTML créée par un appel à la méthode RenderBeginTag. Cette dernière, pour sa part, génère une balise HTML ouvrante qui sera fermée par un appel à la méthode RenderEndTag.

Une autre amélioration que nous pouvons apporter à notre contrôle est l'utilisation d'une propriété de type Style.
La classe Style est fournie dans le framework .NET et permet d'encapsuler les propriétés de style communément appliquées.
Nous pouvons alors modifier le code de notre contrôle de la manière suivante :

Utilisation d'un objet Style comme propriété

public class MonPremierControl : Control
{
private string _text = "Texte par défaut";
public string Text
{
get { return _text; }
set { _text = value; }
}

private Style _style = new Style();
public Style Style
{
get { return _style; }
}

protected override void Render(HtmlTextWriter writer)
{
_style.AddAttributesToRender(writer);
writer.RenderBeginTag(HtmlTextWriterTag.H1);
writer.Write(this._text);
writer.RenderEndTag();
}
}

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register TagPrefix="MyCtl" Namespace="Dvp.Controls" Assembly="MesControles" %>

<html>
<body>
<form runat="server">
<MyCtl:MonPremierControl runat="server" Text="Ce contrôle est personnalisable !" Style-Font-Size="18px" Style-ForeColor="Blue" />
</form>
</body>
</html>

Il est possible d'accéder aux propriétés exposées par l'objet style de deux façons différentes. La méthode utilisée ici est celle de l'« objectWalker » qui consiste à séparer la propriété de la sous-propriété par un -. La seconde méthode utilisable est celle des éléments imbriqués. Dans cette méthode, les propriétés sont définies sous la forme de balises définies entre la balise ouvrante et la balise fermante de l'élément auquel elles se rapportent.

Les plus observateurs auront remarqué que la propriété Style est en lecture seule. C'est suffisant, car nous ne souhaitons pas modifier l'objet exposé, mais bien les propriétés que lui-même expose.

Au travers de cette section, nous venons de voir comment utiliser les facilités du framework pour rendre le code de notre contrôle plus lisible, mais surtout plus facile à écrire.

IV - WebControl plutôt que Control

Plutôt que d'hériter de la classe Control pour notre contrôle nous allons maintenant hériter de la classe WebControl qui nous apporte les facilités suivantes :

- l'objet « Style » est intégré et les propriétés courantes de présentation sont exposées ;

- la classe WebControl assure la conservation des paramètres/états au cours des PostBacks grâce au ViewState (avec la classe Control, cela aurait dû être fait manuellement en surchargeant les méthodes LoadViewState et SaveViewState) ;

- la classe WebControl autorise les attributs expando dans la déclaration du contrôle (ce qui provoquerait une exception avec la classe Control).

Cependant, hériter de la classe WebControl implique également les changements suivants :

- la suppression des propriétés de Style (intégrées maintenant) ;

- la définition d'un constructeur public invoquant le constructeur de base en précisant l'élément qui devra être rendu ;

- effacer la méthode Render et surcharger la méthode RenderContent pour générer le contenu désiré de notre contrôle. On ne s'occupe plus que du contenu, la classe WebControl prenant en charge la présentation des attributs et des balises de début / fin.

Notre contrôle ressemble alors à ceci :

using System;
using System.Web;
using System.Web.UI;
using System.Drawing;
using System.Web.UI.WebControls;

namespace Dvp.Controls
{
public class MonPremierControl : WebControl
{
private string _text = "Texte par défaut";

public string Text
{
get { return _text; }
set { _text = value; }
}

public MonPremierControl() : base(HtmlTextWriterTag.H1) { }

protected override void RenderContents(HtmlTextWriter writer)
{
writer.Write(_text);
}
}
}

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register TagPrefix="MyCtl" Namespace="Dvp.Controls" Assembly="MesControles" %>

<html>
<body>
<form runat="server">
<MyCtl:MonPremierControl runat="server" Text="Ce contrôle est personnalisable !" Font-Size="18px" ForeColor="Blue" />
</form>
</body>
</html>

V - Création d'un Textbox

Nous venons de voir dans les sections précédentes comment créer un contrôle serveur et interagir sur son apparence et son contenu.
Nous allons maintenant voir comment créer un contrôle avec lequel nous allons interagir, à savoir un contrôle de type TextBox.
Commençons par créer notre contrôle. Nous allons nous baser sur la classe WebControl dont nous invoquerons le constructeur en passant input comme argument afin de créer un contrôle HTML du type input.
Voici à quoi ressemble le code de notre contrôle :

public class MyTextBox : WebControl
{
public MyTextBox()
: base("input")
{ }

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
}
}

Voici à quoi il ressemble :

Notre contrôle TextBox

Et voici le code HTML qui est généré :

<html>
<body>
<form name="ctl00" method="post" action="default.aspx" id="ctl00">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE0NjkwMTU5MTdkZKsBqYU/CXTBApOlNyNTSv9WSS0e" />
</div>

Voici mon textbox : <input type="text" name="ctl01" />
</form>
</body>

</html>

On peut directement remarquer que nous avons ajouté un attribut Name à notre contrôle. Cet attribut est nécessaire pour assurer le fonctionnement du mécanisme de PostBack.
Ajoutons un bouton à la page qui contient notre contrôle, inscrivons quelque chose dans notre textbox et cliquons sur le bouton pour voir ce qu'il se passe.

Notre textbox, il ne nous reste plus qu'à cliquer sur le bouton

Nous avons perdu le contenu de notre textbox après le postback

Comme illustré sur les deux captures précédentes, le texte de notre textbox n'est pas conservé lors du PostBack.
Pour remédier à cela, nous allons ajouter un attribut value à notre contrôle afin de pouvoir en spécifier le contenu. Mais cet attribut seul ne changera rien à notre problème. Il nous faut en effet également implémenter l'interface IPostBackDataHandler afin de pouvoir utiliser les données du PostBack.
Voici à quoi ressemble maintenant le code de notre contrôle :

public class MyTextBox : WebControl, IPostBackDataHandler
{
private string _value = null;

public MyTextBox()
: base("input")
{ }

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
if (!string.IsNullOrEmpty(_value))
{
writer.AddAttribute(HtmlTextWriterAttribute.Value, _value);
}
}

public string Value
{
get { return _value; }
set { _value = value; }
}

#region IPostBackDataHandler Membres

public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
_value = postCollection[postDataKey];
return false;
}

public void RaisePostDataChangedEvent()
{

}

#endregion
}

La méthode qui nous intéresse ici est la méthode LoadPostData. En premier paramètre, cette méthode reçoit la clé à utiliser pour récupérer les données de PostBack de notre contrôle dans la collection postCollection. Cette collection, quant à elle, contient toutes les données PostBack des contrôles présents dans la page.
La méthode RaisePostDataChangedEvent quant à elle, n'est appelée que si la méthode LoadPostData renvoie true. C'est dans cette méthode que nous pouvons faire déclencher des événements relatifs aux données postback de notre contrôle (par exemple, lorsque le texte change).
Il serait légitime de se demander pourquoi ne pas directement publier les événements dans la méthode LoadPostData. En fait, la méthode RaisePostDataChangedEvent n'est appelée que lorsque la méthode LoadPostData a été appelée pour tous les contrôles. Cela garantit donc que tous les contrôles ont effectivement reçu leurs données postback.

La méthode LoadPostData est invoquée juste après le chargement du ViewState et donc entre le OnInit et le OnLoad. Il est donc normal que les données PostBack ne soient pas accessibles avant le OnLoad.

Nous allons maintenant utiliser la méthode RaisePostDataChangeEvent afin de publier un événement signalant que le texte a changé.
Voici à quoi ressemble alors le code de notre contrôle :

public class MyTextBox : WebControl, IPostBackDataHandler
{
public MyTextBox()
: base("input")
{ }

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
if (!string.IsNullOrEmpty(this.Text))
{
writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
}
}

#region IPostBackDataHandler Membres

public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
bool _fireEvent = false;
if (this.Text != postCollection[postDataKey])
{
_fireEvent = true;
}
this.Text = postCollection[postDataKey];
return _fireEvent;
}

public void RaisePostDataChangedEvent()
{
if (TextChanged != null)
{
TextChanged(this, EventArgs.Empty);
}
}

#endregion

public event EventHandler TextChanged;

public string Text
{
get
{
if (ViewState["text"] != null)
{
return (string)ViewState["text"];
}

return string.Empty;
}
set { ViewState["text"] = value; }
}
}

Nous utilisons maintenant dans notre contrôle le ViewState afin de conserver la valeur de notre contrôle à travers les PostBack.
Dans la méthode LoadPostData, nous vérifions que la valeur que nous recevons via le PostBack est bien la même que celle mémorisée dans le ViewState. Si ce n'est pas le cas, c'est que le texte a changé, et donc nous levons l'événement TextChanged.
Illustrons cela par un exemple.
Voici le code que nous ajoutons dans la page afin de vérifier que l'événement est bien déclenché :

<html>
<body>
<form runat="server">
Voici mon textbox : <MyCtl:MyTextBox id="MyTB" runat="server" />
<br />
<br />
<asp:Button runat="server" Text="Go..." />
</form>
</body>
</html>

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
MyTB.TextChanged += new EventHandler(MyTB_TextChanged);
}

void MyTB_TextChanged(object sender, EventArgs e)
{
Response.Write("Le texte a changé");
}
}

Nous précisons un ID pour notre contrôle puis, dans le Page_Load, nous nous abonnons à l'événement TextChanged de notre contrôle. Voici ce que cela donne :

Remplissons notre TextBox

Comme le texte a changé, l'événement est levé

Si, lorsqu'on clique sur le bouton le texte de notre contrôle a changé, lors du PostBack, l'événement TextChanged est déclenché et le texte « Le texte à changé » apparaît sur la page. Si le contenu du TextBox n'a pas changé, rien ne se passe.

VI - PostBack automatique

Par défaut, les contrôles du type Bouton (Button, ImageButton, LinkButton) déclenchent un PostBack. Cependant, il est tout à fait envisageable de faire déclencher un PostBack à un autre contrôle.
Nous allons améliorer notre contrôle TextBox pour qu'il déclenche automatiquement un PostBack si son contenu change.
Premièrement, il faut implémenter l'interface IPostBackEventHandler et donc la méthode RaisePostBackEvent(string args). Deuxièmement, il faut ajouter au rendu HTML de notre contrôle le code JavaScript nécessaire pour provoquer le PostBack. On utilise pour cela la méthode GetPostBackEventReference.
Cette méthode attend deux paramètres. Le premier est le contrôle à la source du PostBack, le second est l'argument qui sera transmis lors du PostBack.
Voici le code modifié de notre contrôle :

public class MyTextBox : WebControl, IPostBackDataHandler, IPostBackEventHandler
{
public MyTextBox()
: base("input")
{ }

protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
if (!string.IsNullOrEmpty(this.Text))
{
writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
}
if (_autoPostBack)
{
writer.AddAttribute(HtmlTextWriterAttribute.Onchange, "JavaScript:" + Page.ClientScript.GetPostBackEventReference(this, ""));
}
}

#region IPostBackDataHandler Membres

public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
bool _fireEvent = false;
if (this.Text != postCollection[postDataKey])
{
_fireEvent = true;
}
this.Text = postCollection[postDataKey];
return _fireEvent;
}

public void RaisePostDataChangedEvent()
{
if (TextChanged != null)
{
TextChanged(this, EventArgs.Empty);
}
}

#endregion

#region IPostBackEventHandler Membres

public void RaisePostBackEvent(string eventArgument)
{

}

#endregion

public event EventHandler TextChanged;

public string Text
{
get
{
if (ViewState["text"] != null)
{
return (string)ViewState["text"];
}

return string.Empty;
}
set { ViewState["text"] = value; }
}

private bool _autoPostBack = false;
public bool AutoPostBack
{
get { return _autoPostBack; }
set { _autoPostBack = value; }
}
}

Nous y avons également intégré une nouvelle propriété appelée AutoPostBack qui permet à l'utilisateur de déterminer si notre contrôle doit ou non provoquer le PostBack automatiquement lorsque le contenu du textbox change.
Voici le code HTML généré :

<html>
<body>
<form name="ctl00" method="post" action="Default.aspx" id="ctl00">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE0NjkwMTU5MTdkZKsBqYU/CXTBApOlNyNTSv9WSS0e" />
</div>

<div>

<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />

</div>
Voici mon textbox : <input id="MyTB" type="text" name="MyTB" onchange="javascript:__doPostBack('MyTB','')" />

<script type="text/JavaScript">
var theForm = document.forms['ctl00'];
if (!theForm) {
theForm = document.ctl00;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
</script>

</form>
</body>
</html>

On peut voir que l'attribut onchange de notre contrôle appelle une fonction JavaScript __doPostBack en passant comme argument l'id du contrôle et une chaîne vide (qui correspond aux paramètres que nous aurions pu préciser lors de l'appel à la méthode GetPostBackEventReference.
Si on change le contenu du textbox et qu'on valide ce changement (appui sur la touche TAB ou ENTER par exemple), on pourra remarquer que le PostBack est déclenché.

Dans ce cas-ci, il ne nous a pas été nécessaire de passer un argument lors de l'appel à la fonction __doPostBack. Si cela avait été nécessaire, il nous aurait suffi de préciser en second argument de la méthode GetPostBackEventReference l'argument que nous voulions voir passer comme second paramètre lors de l'appel à la méthode __doPostBack. Ce paramètre est alors récupérable comme argument de la méthode RaisePostBackEvent.

Ajoutons deux liens à notre contrôle. Le premier aura pour but de mettre le contenu du TextBox en Majuscules, le second, vous l'aurez deviné, en Minuscules.
Voici à quoi ressemble maintenant le code de notre contrôle :

public class MyTextBox : WebControl, IPostBackDataHandler, IPostBackEventHandler
{
protected override void Render(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Type, "text");
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
if (!string.IsNullOrEmpty(this.Text))
{
writer.AddAttribute(HtmlTextWriterAttribute.Value, this.Text);
}
if (_autoPostBack)
{
writer.AddAttribute(HtmlTextWriterAttribute.Onchange, "JavaScript:" + Page.ClientScript.GetPostBackEventReference(this, ""));
}
writer.RenderBeginTag(HtmlTextWriterTag.Input);
writer.RenderEndTag();
writer.Write("&nbsp;<a href=\"JavaScript:" + Page.ClientScript.GetPostBackEventReference(this, "upper") + "\">Maj</a>");
writer.Write("&nbsp;-&nbsp;");
writer.Write("<a href=\"JavaScript:" + Page.ClientScript.GetPostBackEventReference(this, "lower") + "\">Min</a>");
}

#region IPostBackDataHandler Membres

public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
bool _fireEvent = false;
if (this.Text != postCollection[postDataKey])
{
_fireEvent = true;
}
this.Text = postCollection[postDataKey];
return _fireEvent;
}

public void RaisePostDataChangedEvent()
{
if (TextChanged != null)
{
TextChanged(this, EventArgs.Empty);
}
}

#endregion

#region IPostBackEventHandler Membres

public void RaisePostBackEvent(string eventArgument)
{
switch (eventArgument)
{
case "upper":
this.Text = this.Text.ToUpper();
break;
case "lower":
this.Text = this.Text.ToLower();
break;
}
}

#endregion

public event EventHandler TextChanged;

public string Text
{
get
{
if (ViewState["text"] != null)
{
return (string)ViewState["text"];
}

return string.Empty;
}
set { ViewState["text"] = value; }
}

private bool _autoPostBack = false;
public bool AutoPostBack
{
get { return _autoPostBack; }
set { _autoPostBack = value; }
}
}

Plutôt que d'appeler le constructeur de la classe de base en lui passant le type de l'élément HTML que nous souhaitions créer, nous avons ici surchargé la méthode Render afin d'ajouter au rendu HTML de notre contrôle deux liens dans lesquels nous inscrivons les instructions nécessaires pour provoquer le PostBack en passant un paramètre (« upper » et « lower »).
Dans la méthode RaisePostBackEvent, nous vérifions le contenu de l'argument reçu et nous traitons le contenu du TextBox en fonction.
Voici à quoi ressemble maintenant le code HTML généré par notre contrôle :

<input id="MyTB" type="text" name="MyTB" />&nbsp;
<a href="javascript:__doPostBack('MyTB','upper')">Maj</a>&nbsp;-&nbsp;
<a href="javascript:__doPostBack('MyTB','lower')">Min</a>

On y retrouve les deux liens avec les appels à la méthode __doPostBack.
Voici à quoi ressemble le contrôle :

Notre contrôle avant de cliquer sur un lien

Après avoir cliqué sur le lien 'Maj'

Et après avoir cliqué sur le lient 'Min'

Comme vous l'aurez remarqué, il est relativement simple, via ce mécanisme, de provoquer un PostBack depuis n'importe quel événement JavaScript.

VII - Conclusion

Nous venons de voir à travers ces quelques paragraphes comment créer un contrôle serveur ASP.NET simple.
Pour ceux qui souhaitent approfondir le sujet, je vous invite à lire les articles de nicopyright(c) concernant les contrôles Templates ou le mécanisme de création des contrôles ASP.NET et leur cycle de vie ou encore l'article de Keihilin Une TextBox qui s'autovalide.

VIII - Remerciements

Merci à l'équipe DotNet, à ses vaillants relecteurs et plus particulièrement à NicoPyright(c) et Tomlev.