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'agît 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.

1. La création d'un DataGrid

1.1. 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 lui permet beaucoup plus, soit vous êtes très motivés (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.

 
Sélectionnez

<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>

1.2. 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" votre DataGrid; mais une fois de plus, ce n'est pas l'objet de ce tutoriel.

Création d'un DataGrid dans le CodeBehind
Sélectionnez

 		' 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)


        ' ------ Etudions la création de cette colonne ------

        ' Les données affichées dans cette colone sont récupérées dans 
		'  le DataReader dans le champ NOM
		
        colNom.DataField = "NOM"

        ' On définit le texte d'en-tête de la colonne
        colNom.HeaderText = "Descriptif du produit"

        ' On définit la largeur de la colonne
        colNom.HeaderStyle.Width = Unit.Pixel(650)

        ' On mets en gras le corps du texte de l'en-tête
        colNom.HeaderStyle.Font.Bold = True

        ' On aligne le texte de l'en-tê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 
		'  peuevent ê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.

2. Exportation d'un DataGrid simple

2.1. 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'agît d'une feuille de calcul Excel, et non d'un fichier de données quelconques, 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
  • Envoie 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 :

 
Sélectionnez

<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

2.2. Le code expliqué

Code d'exportation d'un DataGrid simple
Sélectionnez

    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'informations 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 envoie les données au client
        Response.End()


    End Sub

3. Exportation d'un DataGrid complexe

3.1. 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.

3.2. Le code commenté

Le code d'exportation ne change pas, à l'exception de l'appel de verifColonne( ) qui va effectuer le traitement.

Code d'exporation d'un DataGrid compelxe
Sélectionnez

    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'informations renvoyée, ici des données au format Excel
        Response.ContentType = "application/vnd.ms-excel"

        ' On efface le jeux 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 envoi 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'agît d'une cellule standard (contenant du texte) alors aucun traitement ne sera effectué. S'il ne s'agît 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.
Code de traitement du contenu du DataGrid
Sélectionnez


    Private Sub verifColonne(ByVal monDataGrid As Control)

        ' On récupère le nombre de controles 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 controle concerné à une prorpiété "Selected Item" alors...
            ' Remarque : Seul les controles 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 controle concerné est retiré
                monDataGrid.Parent.Controls.Remove(monDataGrid)


            Else

                ' Si le controle 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 !