FAQ ASP.NET/C#
FAQ ASP.NET/C#Consultez toutes les FAQ
Nombre d'auteurs : 39, nombre de questions : 371, dernière mise à jour : 15 juin 2021
- Comment rafraichir un page web à intervalle régulier en code-behind ?
- Comment vider le cache d'une page aspx ?
- Comment créer dynamiquement le titre de la page ?
- Comment modifier le head et le body d'une page aspx ?
- Peut-on avoir plusieurs formulaires avec runat=server dans une même page ?
- Comment ajouter dynamiquement des contrôles à une page ?
- A quoi sert AutoEventWireup ?
- Comment positionner l'ascenseur d'une page web en code-behind ?
- Comment définir la page utilisée en cas d'erreur pour une page précise?
- Comment modifier la couleur de fond d'une page web par le code?
- Pourquoi ai-je une erreur injection de script ?
- Comment maintenir le scroll après un postback ?
- Comment poster automatiquement la page en javascript ?
- Comment simuler un click de bouton pour poster une page ?
- 4.2.1. Cycle de vie (1)
On peut facilement rafraichir une page à intervale régulier grâce à du javascript. Mais quand il faut faire ça en code-behind car la fréquence de rafraichissement est variable ou dépend d'une condition, au lieu de se lancer dans des fonction javascript complexe avec une gestion de timer, il existe une fonction toute simple en ASP.NET :
Response.
AppendHeader
(
"Refresh"
,
"1"
);
Le temps est exprimé en seconde et on ne peut pas utiliser d'unité de temps plus petite.
Pour vider le cache d'une page aspx il suffit d'exécuter ces quelques lignes :
Response.
CacheControl =
"no-cache"
;
Response.
AddHeader
(
"Pragma"
,
"no-cache"
);
Response.
ExpiresAbsolute =
DateTime.
Now.
Date;
Response.
Expires =
-
1
;
ou
Response.
Cache.
SetExpires
(
DateTime.
Now);
ou encore la solution html:
Voyons comment on créer dynamiquement le titre d'une page : on parle ici de ce que contient la balise < title>. dans la page aspx :
<title id
=
"titrePage"
runat
=
server />
on déclare dans le code-behind :
protected
HtmlGenericControl titrePage;
pour donner une valeur au titre de la page :
titrePage.InnerText
=
"FAQ ASP.NET"
;
Pour modifier l'entête (head) et le corps (body) d'une page, il faut leur assigner un id et définir la propriété runat à "server".
<head id
=
"head"
runat
=
"server"
>
<body id
=
"body"
runat
=
"server"
>
ensuite, les déclarer dans le code-behind comme étant des HtmlGenericControl, c'est à dire
protected
HtmlGenericControl body;
protected
HtmlGenericControl head;
head =
Page.
FindControl
(
"head"
);
// on recupère le head de la page
head.
InnerHtml +=
"Ici le texte que je peux ajouter dans le <head> de ma page"
;
// pareil pour le body
body =
Page.
FindControl
(
"body"
);
body.
Attributes
(
"onclick"
) =
"fctjavascript()"
;
// ici je rajoute un attribut
Non.
Mais pourquoi ne peut-on pas avoir plusieurs formulaires avec runat=server sur une même page ?
Il serait d'ailleurs plus judicieux de préciser qu'on ne peut pas avoir plusieurs formulaires visible sur la même page.
En effet :
<
form id=
"form1"
runat=
"server"
>
</
form>
<
form id=
"form2"
runat=
"server"
visible=
"false"
>
</
form>
ne posera pas de problème d'exécution, tandis que :
<
form id=
"form1"
runat=
"server"
>
</
form>
<
form id=
"form2"
runat=
"server"
>
</
form>
levera l'HttpException suivante :
A page can have only one server-side Form tag.
Le modèle de programmation à formulaire unique (Single Form Model) est fait de manière
à n'avoir qu'une seule balise form visible, avec l'attribut runat = server. Cela permet à ASP.NET de gérer les contrôles qui sont posés dans le formulaire,
ayant la balise runat=server, coté serveur. Cela permet entre autre de pouvoir y accéder dans le code behind directement.
Le modèle de développement impose que les éléments de formulaire soient postés à la même page qui les a soumis, ce qui permet aux mécanismes d'ASP.NET (ViewState, etc ...)
de fonctionner correctement.
Pour la curiosité, la pile d'appel au moment de l'exception nous permet de constater que l'exception est levée au moment de l'appel à la méthode System.Web.UI.Page.OnFormRender().
Un petit coup de reflector nous permet de voir :
internal
void
OnFormRender
(
)
{
if
(
this
.
_fOnFormRenderCalled)
{
throw
new
HttpException
(
SR.
GetString
(
"Multiple_forms_not_allowed"
));
}
this
.
_fOnFormRenderCalled =
true
;
this
.
_inOnFormRender =
true
;
}
Cette méthode OnFormRender est appelée au moment du rendu du controle HtmlForm. Le code issu de Reflector nous permet bien de constater qu'un boolean est mis à vrai lors du rendu d'un controle HtmlForm. Si ce boolean est déjà à vrai, alors l'exception est levée.
Afin de voir comment on ajoute dynamiquement un WebControl à une page, nous allons prendre un exemple simple : ajouter un label à une page aspx.
Dans la page aspx, on ajoute un PlaceHolder :
<asp:PlaceHolder id
=
"PlaceHolder1"
runat
=
"server"
></asp:PlaceHolder>
dans le code-behind
protected
PlaceHolder PlaceHolder1;
private
void
Page_Load
(
System.
object
sender,
System.
EventArgs e)
{
Label monlabel =
new
Label
(
);
PlaceHolder1.
Controls.
Add
(
monlabel);
}
Vous avez là le code minimal pour ajouter un contrôle dynamiquement à une page aspx. Vous pouvez ensuite jouer sur les différentes propriétés et méthodes du WebControl pour l'initialiser comme vous souhaitez.
Si l'on définit dans sa page aspx AutoEventWireup à True
<%
@ Page Language=
"C#"
AutoEventWireup=
"true"
CodeFile=
"Default.aspx.cs"
Inherits=
"Default"
%>
on force un mapping de certains événements de la page. Ainsi par exemple, la méthode Page_Load sera appelée après la méthode OnLoad.
Ce mapping est un confort d'utilisation pour éviter d'avoir à surcharger les méthodes de la page ; mais c'est aussi une hérésie en termes de performances. Le framework va user de reflexions et de délégates simplement pour nous éviter une surcharge.
Préferez sans hésiter les surcharges et mettez la propriété AutoEventWireup à false;
<%
@ Page Language=
"C#"
AutoEventWireup=
"fase"
CodeFile=
"Default.aspx.cs"
Inherits=
"Default"
%>
protected
override
void
OnLoad
(
EventArgs e)
{
// je fais qqchose
base
.
OnLoad
(
e);
}
au lieu de :
<%
@ Page Language=
"C#"
AutoEventWireup=
"true"
CodeFile=
"Default.aspx.cs"
Inherits=
"Default"
%>
protected
void
Page_Load
(
object
sender,
EventArgs e)
{
// je fais qqchose
}
Voici une petite astuce qui vous permet de positionner l'ascenceur d'une page web au niveau d'un WebControl.
Tout d'abord on crée un WebControl, un label sans texte par exemple qui sera invisible sur la page web.
Ensuite, dans votre page aspx :
<script>
location
=
'
#<% Response.Write(varpos) %>
'
;
</script>
Dans le code-behind il suffit ensuite de faire :
public
string
varpos;
varpos =
"MonWebControl"
;
...et l'ascenceur sera possitionné au niveau du label appellé "MonWebControl".
Tout comme on l'aurait fait pour définir la page d'erreur pour toutes les pages de l'application, il faut modifier le web.config afin d'y trouver
<customErrors
mode
=
"On"
/>
Ensuite, pour la page concernée, au début du fichier .aspx,
Il est nécessaire de faire "le lien" entre la page et le code-behind.
<body id="Body" runat="server">
et dans le code-behind:
Body.
Style[
"background-color"
]
=
"#FF0000"
;
On a parfois besoin d'envoyer des informations telles que du xml, html ou tout autre chose
contenant <...>. Par défaut, ASP.NET refuse l'envoi de ces tags et l'informe par l'intermédiaire
d'une exception qui signale que, par mesure de sécurité, il est interdit d'envoyer de telles choses au
serveur. Effectivement, en autorisant cet envoi, il serait possible d'injecter du javascript par exemple.
Pour permettre l'envoi de ces informations, il est nécessaire de mettre validateRequest="false" dans la
directive "Page".
Une fois ces informations envoyées, il faut encore préciser au serveur qu'il ne doit pas les interpréter
mais bien garder cela comme du texte. Ceci se fait par l'intermédiaire de
string
html =
Server.
HtmlEncode
(
TextBox1.
Text);
Il existe une propriété qui n'est pas très connue et pourtant bien pratique pour repositionner le scroll d'une page à l'endroit où elle était avant un postback.
Il s'agit de la propriété MaintainScrollPositionOnPostback.
Imaginons une page avec beaucoup de contenu ...
<%
@ Page MaintainScrollPositionOnPostback=
"true"
Language=
"C#"
AutoEventWireup=
"false"
CodeBehind=
"Default.aspx.cs"
Inherits=
"testScroll._Default"
%>
<!
DOCTYPE html PUBLIC "-W3CDTD XHTML 1.0 TransitionalEN"
"http:www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<
html xmlns=
"http://www.w3.org/1999/xhtml"
>
<
head runat=
"server"
>
<
title>
Untitled Page</
title>
</
head>
<
body>
<
form id=
"form1"
runat=
"server"
>
<
asp:
Label runat=
"server"
Text=
"<%#DateTime.Now.ToShortTimeString() %>"
/><
br/>
<
p>
ici mettre plein de trucs histoire d'avoir une barre de défilement
</
p>
<
br />
<
br />
<
asp:
Button runat=
"server"
Text=
"Valider"
/>
</
form>
</
body>
</
html>
L'utilisation de la propriété MaintainScrollPositionOnPostback (positionnée à true) sur la page permet de rajouter automatiquement du javascript qui se chargera de re-positionner correctement le scroll à l'endroit où on l'a laissé.
Pour que le framework.net puisse appréhender correctement le postback d'une page, il va falloir utiliser la méthode javascript _doPostBack(...).
Si on utilise une quelconque autre méthode (form.submit(), etc ...), cela ne sera pas géré correctement.
On va alors utiliser la méthode GetPostBackEventReference pour générer correctement la fonction _doPostBack.
Prenons l'exemple simpliste d'une dropdownlist qui devra poster la page à chaque changement de sélection (notez que ceci peut être fait automatiquement grâce à la propriété autopostback, mais ce n'est pas le but de la présentation).
soit la page suivante :
<
asp:
DropDownList runat=
"server"
ID=
"maDropDown"
>
<
asp:
ListItem Text=
"Valeur 1"
/>
<
asp:
ListItem Text=
"Valeur 2"
/>
<
asp:
ListItem Text=
"Valeur 3"
/>
</
asp:
DropDownList>
dans le code behind, on associe la méthode javascript onchange à la fonction qui va poster la page. On le construira ainsi :
protected
override
void
OnLoad
(
EventArgs e)
{
maDropDown.
Attributes[
"onchange"
]
=
Page.
ClientScript.
GetPostBackEventReference
(
maDropDown,
maDropDown.
ID);
if
(
IsPostBack)
Response.
Write
(
"la page a été correctement postée à "
+
DateTime.
Now.
ToLongTimeString
(
));
}
Vous pouvez constater que la page est postée à chaque changement de valeur dans la dropdown.
On peut également vérifier que ce postback est conforme en utilisant la propriété __EVENTTARGET comme expliqué ici.
protected
override
void
OnLoad
(
EventArgs e)
{
maDropDown.
Attributes[
"onchange"
]
=
Page.
ClientScript.
GetPostBackEventReference
(
maDropDown,
maDropDown.
ID);
if
(
IsPostBack &&
Request.
Form[
"__EVENTTARGET"
]
!=
null
&&
Request.
Form[
"__EVENTTARGET"
].
Contains
(
maDropDown.
UniqueID))
{
Response.
Write
(
"la page a été correctement postée par la dropdownlist à "
+
DateTime.
Now.
ToLongTimeString
(
));
}
base
.
OnLoad
(
e);
}
Prenons un exemple avec un TextBox et un bouton :
<
asp:
TextBox ID=
"leTextBox"
runat=
"server"
/>
<
asp:
Button ID=
"monBouton"
runat=
"server"
Text=
"go"
OnClick=
"clic"
/>
on a l'événement du click dans le code behind écrit ainsi :
protected
void
clic
(
object
sender,
EventArgs e)
{
Response.
Write
(
"le bouton a été cliqué"
);
}
Comment simuler un clic par exemple lorsque la valeur du textbox change ?
Pour ca, on va utiliser la méthode GetPostBackEventReference.
protected
override
void
OnLoad
(
EventArgs e)
{
leTextBox.
Attributes[
"onchange"
]
=
Page.
ClientScript.
GetPostBackEventReference
(
monBouton,
monBouton.
ID);
base
.
OnLoad
(
e);
}
Si on s'arrete à ca, ASP.NET va nous lever une belle erreur :
Argument de publication ou de rappel non valide. La validation d'événement est activée via <pages enableEventValidation="true"/>
dans la configuration ou via <%@ Page EnableEventValidation="true" %> dans une page. Pour des raisons de sécurité,
cette fonctionnalité vérifie si les arguments des événements de publication ou de rappel proviennent du contrôle serveur qui les
a rendus à l'origine. Si les données sont valides et attendues, utilisez la méthode ClientScriptManager.RegisterForEventValidation
afin d'inscrire les données de publication ou de rappel pour la validation.
Cette erreur provient du fait qu'ASP.NET effectue un contrôle sur le POST pour détecter d'éventuelles attaques d'injections ou d'altération de la requete POST. C'est le principe d'event validation.
Une solution pour le désactiver est de mettre la propriété EnableEventValidation à false dans la directive de Page.
Ceci aura pour effet de désactiver complètement ce processus de validation, ce qui peut être nécessaire dans certain cas, mais ne permet plus cette vérification automatique pour toute la page (ceci ne peut pas être fait contrôle par contrôle).
L'autre solution plus propre est d'indiquer à ASP.NET que ce postpack est tout à fait autorisé. C'est ce que permet l'utilisation de la méthode RegisterForEventValidation.
Cet appel ne pourra être fait qu'en surchargeant la méthode Render de la page :
protected
override
void
Render
(
HtmlTextWriter writer)
{
Page.
ClientScript.
RegisterForEventValidation
(
monBouton.
UniqueID,
monBouton.
ID);
base
.
Render
(
writer);
}
Désormais, le post est bien pris en compte et la méthode clic associée au bouton est bien lancée.