Introduction▲
Excel étant un outil très utilisé, il peut être utile d'exporter des données depuis une page ASP.Net (nous utiliserons le langage VB.Net) vers une feuille Excel. Nous allons voir ici qu'il s'agit d'une chose aisée en ASP.Net. Cela était déjà possible avec ASP (ce cher ancêtre n'ayant par ailleurs plus grand-chose à voir avec son successeur), mais au prix d'un bon mal de tête.
Il n'est désormais plus nécessaire de passer par un composant COM, quelques lignes de code suffisent. Le composant qui s'apparente le plus à une feuille de calcul Excel est le DataGrid. C'est donc tout naturellement lui que nous allons utiliser pour exporter nos données.
Je tiens à préciser que tout cela a été testé avec le Framework 1.1 (SP1) et Excel 2003, je ne peux donc certifier le fonctionnement avec une plateforme différente.
I. La création d'un DataGrid▲
I-A. Dans la page ASPX▲
Pour créer un DataGrid dans une page ASPX, il y a deux voies possibles : soit l'on utilise une solution WYSIWYG (What You See Is What You Get) comme Web Matrix, ou Visual Studio qui permet beaucoup plus, soit vous êtes très motivé (trop ?) et vous le faites à la main en tapant les balises une à une…
Ce tutoriel n'ayant pas pour but la création de DataGrid, je vais ici me contenter de vous donner un copier/coller du code permettant d'afficher un tel composant dans une page ASPX.
<
asp
:
DataGrid
ID
=
"dgCommande"
runat
=
"server"
AutoGenerateColumns
=
"False"
BackColor
=
"White"
BorderColor
=
"Black"
CellPadding
=
"3"
Font-Name
=
"Verdana"
HeaderStyle-BackColor
=
"#cfcfcf"
EnableViewState
=
"False"
Font-Size
=
"8pt"
Font-Names
=
"Verdana"
>
<AlternatingItemStyle
BackColor
=
"#D3E2EF"
></AlternatingItemStyle>
<HeaderStyle
Font-Bold
=
"True"
HorizontalAlign
=
"Center"
BackColor
=
"#FFFFC0"
></HeaderStyle>
<Columns>
<
asp
:
HyperLinkColumn
DataNavigateUrlField
=
"refarticle"
DataNavigateUrlFormatString
=
"detail.aspx?id={0}"
DataTextField
=
"refarticle"
HeaderText
=
"Référence"
>
<HeaderStyle
HorizontalAlign
=
"Center"
Width
=
"35px"
></HeaderStyle>
<ItemStyle
HorizontalAlign
=
"Center"
VerticalAlign
=
"Middle"
></ItemStyle>
</
asp
:
HyperLinkColumn>
<
asp
:
BoundColumn
DataField
=
"produit"
HeaderText
=
"Produit"
>
<HeaderStyle
HorizontalAlign
=
"Center"
Width
=
"200px"
></HeaderStyle>
<ItemStyle
HorizontalAlign
=
"Center"
></ItemStyle>
</
asp
:
BoundColumn>
<
asp
:
BoundColumn
DataField
=
"quantite"
HeaderText
=
"Quantité"
>
<HeaderStyle
HorizontalAlign
=
"Center"
Width
=
"25px"
></HeaderStyle>
<ItemStyle
HorizontalAlign
=
"Center"
></ItemStyle>
</
asp
:
BoundColumn>
<
asp
:
BoundColumn
DataField
=
"prixht"
HeaderText
=
"Prix HT"
>
<HeaderStyle
HorizontalAlign
=
"Center"
Width
=
"75px"
></HeaderStyle>
<ItemStyle
HorizontalAlign
=
"Center"
></ItemStyle>
</
asp
:
BoundColumn>
<
asp
:
BoundColumn
DataField
=
"PrixTTC"
HeaderText
=
"Prix TTC"
>
<HeaderStyle
HorizontalAlign
=
"Center"
Width
=
"75px"
></HeaderStyle>
<ItemStyle
HorizontalAlign
=
"Center"
></ItemStyle>
</
asp
:
BoundColumn>
<
asp
:
BoundColumn
DataField
=
"refarticle"
HeaderText
=
"Code barre"
>
<HeaderStyle
HorizontalAlign
=
"Center"
Width
=
"85px"
></HeaderStyle>
<ItemStyle
Font-Size
=
"18pt"
Font-Names
=
"Code 3 de 9"
HorizontalAlign
=
"Center"
></ItemStyle>
</
asp
:
BoundColumn>
</
asp
:
DataGrid>
I-B. Dans le CodeBehind▲
Cette technique a ma préférence, en effet, créer le DataGrid directement dans le code permet, selon moi, de mieux maîtriser l'apparence du composant. De plus, il est beaucoup plus facile de le maintenir et de le rendre dynamique.
Nous allons maintenant voir un morceau de code commenté en VB.Net permettant de créer un DataGrid, de le lier à des données et de modifier son apparence. Votre page ASPX doit déjà contenir un DataGrid nommé monDataGrid. Vous pourriez aussi utiliser un composant PlaceHolder, qui est un conteneur vide que l'on peut positionner où l'on veut dans sa page, et dans lequel on peut placer le composant que l'on souhaite (DataGrid, bouton, lien…) pour ne pas avoir à créer « par avance » un DataGrid. Mais une fois de plus, ce n'est pas l'objet de ce tutoriel.
' La source de données du DataGrid est un OracleDataReader portant le nom monReader
monDG.DataSource
=
monReader
' Définition des caractéristiques graphiques générales du DataGrid
monDG.BorderColor
=
Color.Black
monDG.CellPadding
=
3
monDG.CellSpacing
=
0
monDG.HeaderStyle.ForeColor
=
Color.Black
monDG.HeaderStyle.HorizontalAlign
=
HorizontalAlign.Center
monDG.HeaderStyle.BackColor
=
Color.FromName
(
"FFCC33"
)
monDG.AutoGenerateColumns
=
False
monDG.AlternatingItemStyle.BackColor
=
Color.FromName
(
"FFFF99"
)
' Instanciation des colonnes du DataGrid
' (on voit qu'elles sont de types différents)
Dim
colRef As
New
HyperLinkColumn
Dim
colNom As
New
BoundColumn
Dim
colStock As
New
BoundColumn
Dim
colHT As
New
BoundColumn
Dim
colTTC As
New
BoundColumn
Dim
toto As
New
ButtonColumn
'Ce DataGrid comportera donc 5 colonnes
toto.ButtonType
=
ButtonColumnType.PushButton
toto.Text
=
"ddjhdj"
monDG.Columns.Add
(
toto)
colRef.DataTextField
=
"CODE"
colRef.DataNavigateUrlField
=
"CODE"
colRef.DataNavigateUrlFormatString
=
"./../details.aspx?ref={0}"
colRef.HeaderText
=
"Référence"
colRef.HeaderStyle.Width
=
Unit.Pixel
(
150
)
colRef.HeaderStyle.Font.Bold
=
True
colRef.HeaderStyle.HorizontalAlign
=
HorizontalAlign.Center
colRef.ItemStyle.HorizontalAlign
=
HorizontalAlign.Center
monDG.Columns.Add
(
colRef)
' ------ Étudions la création de cette colonne ------
' Les données affichées dans cette colonne sont récupérées
' dans le champ NOM du DataReader
colNom.DataField
=
"NOM"
' On définit le texte d'entête de la colonne
colNom.HeaderText
=
"Descriptif du produit"
' On définit la largeur de la colonne
colNom.HeaderStyle.Width
=
Unit.Pixel
(
650
)
' On met en gras le corps du texte de l'entête
colNom.HeaderStyle.Font.Bold
=
True
' On aligne le texte de l'entête au milieu
colNom.HeaderStyle.HorizontalAlign
=
HorizontalAlign.Center
' On aligne les données dans la colonne à gauche
colNom.ItemStyle.HorizontalAlign
=
HorizontalAlign.Left
' On "rattache" la colonne au DataGrid
monDG.Columns.Add
(
colNom)
'---> Il existe bien évidemment beaucoup d'autres propriétés qui
' peuvent être définies dans le codebehind
colStock.DataField
=
"STOCKPHYS"
colStock.HeaderText
=
"En stock"
colStock.HeaderStyle.Width
=
Unit.Pixel
(
100
)
colStock.HeaderStyle.Font.Bold
=
True
colStock.HeaderStyle.HorizontalAlign
=
HorizontalAlign.Center
colStock.ItemStyle.HorizontalAlign
=
HorizontalAlign.Center
monDG.Columns.Add
(
colStock)
colHT.DataField
=
"PRIXBASEHT"
colHT.HeaderText
=
"Prix HT"
colHT.HeaderStyle.Width
=
Unit.Pixel
(
90
)
colHT.HeaderStyle.Font.Bold
=
True
colHT.HeaderStyle.HorizontalAlign
=
HorizontalAlign.Center
colHT.ItemStyle.HorizontalAlign
=
HorizontalAlign.Center
monDG.Columns.Add
(
colHT)
colTTC.DataField
=
"PRIXBASETTC"
colTTC.HeaderText
=
"Prix TTC"
colTTC.HeaderStyle.Width
=
Unit.Pixel
(
90
)
colTTC.HeaderStyle.Font.Bold
=
True
colTTC.HeaderStyle.HorizontalAlign
=
HorizontalAlign.Center
colTTC.ItemStyle.HorizontalAlign
=
HorizontalAlign.Center
monDG.Columns.Add
(
colTTC)
' Les données contenues dans le OracleDataReader sont liées au DataGrid
monDG.DataBind
(
)
Remarque : je pars du principe que vous avez créé un DataReader (ou autre) qui contient les données à afficher dans le DataGrid. En effet cette étape n'est pas l'objet de ce tutoriel.
II. Exportation d'un DataGrid simple▲
II-A. La méthode▲
Nous allons utiliser l'objet Response qui permet de récupérer le flux de sortie d'une page ASP.Net en le plaçant dans une mémoire tampon, puis lors de la fermeture de cet objet les données collectées sont envoyées au client. Pour copier le contenu du DataGrid dans la mémoire tampon, nous allons utiliser une méthode de l'objet DataGrid qui permet en quelque sorte d'extraire son contenu. Il s'agit de la méthode RenderControl. Cette méthode place les informations dans un HtmlTextWriter.
Pour spécifier au client qu'il s'agit d'une feuille de calcul Excel, et non d'un fichier de données quelconque, il faut définir son type MIME. Les types MIME sont issus d'une standardisation des transferts de fichier en utilisant le protocole HTTP.
Voici un récapitulatif simplifié de la méthode employée.
- Récupération des données dans le DataGrid.
- Spécification du type des données (Excel) récupérées.
- Envoi du fichier Excel au client.
Remarque : comme vous avez tous dû en faire les frais un jour, les différents jeux de caractères sont de véritables pièges à développeurs… Bref, même ici vous n'y échapperez pas. En effet, il se peut que vos données soient altérées (les caractères accentués par exemple) lors du passage de la page ASPX à Excel. Pour cela, je n'ai trouvé qu'une seule solution fiable. J'ai défini, dans ma page ASPX, le jeu de caractères qu'elle doit utiliser. Ainsi, j'ai pu conserver mes accents et mes caractères spéciaux dans Excel. Sans définir le jeu utilisé dans ma page ASPX, je perdais systématiquement tous les caractères accentués. Pour cela, dans la balise HEAD, j'ai rajouté « charset=iso-8859-1 » comme suit :
<META
http-equiv
=
"Content-Type"
content
=
"text/html; charset=iso-8859-1"
>
II-B. Le code expliqué▲
Private
Sub
Button1_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) Handles
Button1.Click
' On efface les éventuelles données déjà dans la mémoire tampon
Response
.Clear
(
)
' On place tout ce qui est destiné au client dans la mémoire tampon
Response
.Buffer
=
True
' On définit le type d'information renvoyée, ici des données au format Excel
Response
.ContentType
=
"application/vnd.ms-excel"
' On efface le jeu de caractères actuellement défini
Response
.Charset
=
""
' On instancie deux objets qui vont nous permettre de récupérer les données du DataGrid :
' le StringWriter et l'HtmlTextWriter
Dim
monStringWriter As
StringWriter =
New
StringWriter
Dim
monHtmlTextWriter As
HtmlTextWriter =
New
HtmlTextWriter
(
monStringWriter)
' On extrait le contenu du DataGrid dans l'HtmlTextWriter
monDG.RenderControl
(
monHtmlTextWriter)
' On copie le contenu extrait dans la mémoire tampon
Response
.Write
(
monStringWriter.ToString
(
))
' On ferme le "flux" de données et on envoie les données au client
Response
.End
(
)
End
Sub
III. Exportation d'un DataGrid complexe▲
III-A. La méthode▲
Tout d'abord, il faut se mettre d'accord sur l'expression « DataGrid complexe ». Je fais cet « abus de langage » pour décrire un DataGrid contenant autre chose que du texte. Cela peut, par exemple, être des boutons ou des liens hypertextes.
Que le DataGrid soit simple ou complexe, la méthode d'exportation des données sera rigoureusement la même, sauf que, dans ce dernier cas, il faudra appliquer un traitement aux données avant d'appliquer la méthode RenderControl. Si ce traitement n'est pas effectué alors une exception sera levée.
Le traitement à effectuer consiste à remplacer chaque contrôle non littéral par sa représentation sous forme de texte. Par exemple, un bouton sera remplacé par son texte, idem pour un lien hypertexte, un contrôle offrant des choix multiples sera remplacé par la valeur sélectionnée…
III-B. Le code commenté▲
Le code d'exportation ne change pas, à l'exception de l'appel de verifColonne( ) qui va effectuer le traitement.
Private
Sub
Button1_Click
(
ByVal
sender As
System.Object
, ByVal
e As
System.EventArgs
) Handles
Button1.Click
' On appelle de verifColonne avec le nom du DataGrid concerné en paramètre
verifColonne
(
monDG)
' On efface les éventuelles données déjà dans la mémoire tampon
Response
.Clear
(
)
' On place tout ce qui est destiné au client dans la mémoire tampon
Response
.Buffer
=
True
' On définit le type d'information renvoyée, ici des données au format Excel
Response
.ContentType
=
"application/vnd.ms-excel"
' On efface le jeu de caractères actuellement défini
Response
.Charset
=
""
' On instancie deux objets qui vont nous permettre de récupérer les données du DataGrid :
' le StringWriter et l'HtmlTextWriter
Dim
monStringWriter As
StringWriter =
New
StringWriter
Dim
monHtmlTextWriter As
HtmlTextWriter =
New
HtmlTextWriter
(
monStringWriter)
' On extrait le contenu du DataGrid dans l'HtmlTextWriter
monDG.RenderControl
(
monHtmlTextWriter)
' On copie le contenu extrait dans la mémoire tampon
Response
.Write
(
monStringWriter.ToString
(
))
' On ferme le "flux" de données et on envoie les données au client
Response
.End
(
)
End
Sub
On peut rapidement expliquer le fonctionnement de verifColonne() de la façon suivante : chaque contrôle composant le DataGrid sera testé. S'il s'agit d'une cellule standard (contenant du texte) alors aucun traitement ne sera effectué. S'il ne s'agit pas d'une cellule standard alors deux possibilités :
- si le contrôle concerné a une propriété « SelectedItem » (c'est le cas des contrôles offrant des choix multiples) alors on récupérera la valeur associée. Elle sera ensuite mise dans le DataGrid en lieu et place du contrôle original ;
- si le contrôle concerné n'a pas de propriété « SelectedItem » alors il aura forcément (enfin dans 99 % des cas) une propriété « Text », c'est donc cette propriété qui est récupérée et mise à la place du contrôle origine.
Private
Sub
verifColonne
(
ByVal
monDataGrid As
Control)
' On récupère le nombre de contrôles enfants composant le DataGrid
Dim
nbControls As
Integer
=
monDataGrid.Controls.Count
-
1
While
nbControls >=
0
verifColonne
(
monDataGrid.Controls
(
nbControls))
nbControls =
nbControls -
1
End
While
' Si la cellule ne contient pas du texte simple
If
Not
(
TypeOf
monDataGrid Is
TableCell) Then
' Si le contrôle concerné à une propriété "Selected Item" alors...
' Remarque : Seuls les contrôles offrant des choix multiples ont une propriété "SelectedItem"
If
Not
(
monDataGrid.GetType
(
).GetProperty
(
"SelectedItem"
) Is
Nothing
) Then
Dim
controleLitteral1 As
LiteralControl =
New
LiteralControl
monDataGrid.Parent.Controls.Add
(
controleLitteral1)
Try
' La cellule prend alors pour valeur le texte correspondant à la propriété "SelectedItem"
controleLitteral1.Text
=
_
CType
(
monDataGrid.GetType
(
).GetProperty
(
"SelectedItem"
).GetValue
(
monDataGrid, Nothing
), String
)
Catch
monException As
Exception
' On récupère l'exception en cas de problème
Response
.Write
(
monException.Message
)
End
Try
' Le contrôle concerné est retiré
monDataGrid.Parent.Controls.Remove
(
monDataGrid)
Else
' Si le contrôle concerné n'a pas de propriété "SelectedItem" alors on récupère le
' texte (s'il y en a un) de sa propriété "Text"
If
Not
(
monDataGrid.GetType
(
).GetProperty
(
"Text"
) Is
Nothing
) Then
Dim
controleLitteral2 As
LiteralControl =
New
LiteralControl
monDataGrid.Parent.Controls.Add
(
controleLitteral2)
' On attribue le texte de la propriété "Text" à la cellule concernée
controleLitteral2.Text
=
_
CType
(
monDataGrid.GetType
(
).GetProperty
(
"Text"
).GetValue
(
monDataGrid, Nothing
), String
)
monDataGrid.Parent.Controls.Remove
(
monDataGrid)
End
If
End
If
End
If
End
Sub
Conclusion▲
Voilà, vous savez désormais comment exporter le contenu d'un DataGrid, qu'il soit simple ou plus complexe, vers Excel. Comme d'habitude, avec le Framework .Net, il n'y a rien de bien sorcier. Ici, la difficulté est essentiellement d'ordre algorithmique. Ce tutoriel a pour but de faire une exportation basique et intégrale d'un DataGrid. Libre à vous d'améliorer tout cela. Je vous souhaite de bonnes exportations vers Excel, et vous donne rendez-vous dans un prochain tutoriel.
Un grand merci à FreeGreg pour la relecture de cet article !