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 :
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 :
<%
@ Register TagPrefix=
"MyCtl"
Namespace=
"Dvp.Controls"
Assembly=
"MonControle"
%>
<html>
<body>
<MyCtl:MonPremierControle runat
=
"server"
/>
</body>
</html>
Et voici le résultat :
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.
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.
<%
@ 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>
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 :
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>
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 :
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 :
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 :
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.
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 :
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
(
" <a href=
\"
JavaScript:"
+
Page.
ClientScript.
GetPostBackEventReference
(
this
,
"upper"
) +
"
\"
>Maj</a>"
);
writer.
Write
(
" - "
);
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"
/>
<a href
=
"javascript:__doPostBack('MyTB','upper')"
>
Maj</a>
-
<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 :
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.