о жизни, вебе, друзьях, семье и многом другом…
RSS icon Email icon Home icon
  • Экспорт содержимого GridView в Excel

    Очень часто при разработке веб-приложений на asp.net возникает задача осуществления эскпорта данных из GridView в экселевский файл. При этом каждый начинает придумывать свой велосипед, потому что можно придумать с десяток различных методов как это сделать. Я предлагаю на мой взгляд самый простой вариант.

    Сразу хочу отметить, весь приведенный код написан на VB.NET. Так произошло не потому, что мне так нравится, а потому что так принято в компании, в которой я работаю. Если вы хотите получить этот код на C#, просто воспользуйтесь онлайн конвертером, например, http://www.developerfusion.com/tools/convert/vb-to-csharp/.

    Итак, что нам нужно:

    1. Вспомогательный класс. Я назвал его ExcelExportHelper. Его код приведен ниже

    Imports System
    Imports System.Data
    Imports System.Configuration
    Imports System.IO
    Imports System.Web
    Imports System.Web.Security
    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    Imports System.Web.UI.HtmlControls
    
    Public Class ExcelExportHelper
    
        Public Shared Sub PrepareAndExport(ByVal fileTitle As String, ByVal gv As GridView, ByVal showGrid As Boolean)
            Dim filename As String
            filename = String.Format("{0}_{1}.xls", fileTitle, DateTime.Now().ToString("yyyyMMdd_HHmmss"))
            gv.AllowPaging = False
            gv.DataBind()
            ExcelExportHelper.Export(filename, gv, showGrid)
        End Sub
    
        Public Shared Sub Export(ByVal fileName As String, ByVal gv As GridView, ByVal showGrid As Boolean)
            HttpContext.Current.Response.Buffer = True
            HttpContext.Current.Response.ClearContent()
            HttpContext.Current.Response.ClearHeaders()
            'HttpContext.Current.Response.ContentType = "application/vnd.ms-excel"
            HttpContext.Current.Response.ContentType = "application/ms-excel"
            HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename = " + fileName)
    
            Dim sw As StringWriter = New StringWriter
            Dim htw As HtmlTextWriter = New HtmlTextWriter(sw)
            '  Create a form to contain the grid
            Dim table As Table = New Table
            If (showGrid) Then
                table.GridLines = gv.GridLines
            Else
                table.GridLines = GridLines.None
            End If
    
            '  add the header row to the table
            If (Not (gv.HeaderRow) Is Nothing) Then
                PrepareControlForExport(gv.HeaderRow)
                table.Rows.Add(gv.HeaderRow)
            End If
            '  add each of the data rows to the table
            For Each row As GridViewRow In gv.Rows
                PrepareControlForExport(row)
                table.Rows.Add(row)
            Next
            '  add the footer row to the table
            If (Not (gv.FooterRow) Is Nothing) Then
                PrepareControlForExport(gv.FooterRow)
                table.Rows.Add(gv.FooterRow)
            End If
            '  render the table into the htmlwriter
            table.RenderControl(htw)
            '  render the htmlwriter into the response
            HttpContext.Current.Response.Write(sw.ToString)
            HttpContext.Current.Response.End()
        End Sub
    
        ' Replace any of the contained controls with literals
        Private Shared Sub PrepareControlForExport(ByVal control As Control)
            Dim i As Integer = 0
            Do While (i < control.Controls.Count)
                Dim current As Control = control.Controls(i)
                If (TypeOf current Is LinkButton) Then
                    control.Controls.Remove(current)
                    control.Controls.AddAt(i, New LiteralControl(CType(current, LinkButton).Text))
                ElseIf (TypeOf current Is ImageButton) Then
                    control.Controls.Remove(current)
                    control.Controls.AddAt(i, New LiteralControl(CType(current, ImageButton).AlternateText))
                ElseIf (TypeOf current Is HyperLink) Then
                    control.Controls.Remove(current)
                    control.Controls.AddAt(i, New LiteralControl(CType(current, HyperLink).Text))
                ElseIf (TypeOf current Is DropDownList) Then
                    control.Controls.Remove(current)
                    control.Controls.AddAt(i, New LiteralControl(CType(current, DropDownList).SelectedItem.Text))
                ElseIf (TypeOf current Is CheckBox) Then
                    control.Controls.Remove(current)
                    control.Controls.AddAt(i, New LiteralControl(CType(current, CheckBox).Checked))
                    'TODO: Warning!!!, inline IF is not supported ?
                End If
                If current.HasControls Then
                    PrepareControlForExport(current)
                End If
                i = (i + 1)
            Loop
        End Sub
    End Class

    Вкратце, что делает этот класс: есть два статичных публичных метода: PrepareAndExport и Export.

    Метод PrepareAndExport убирает в GridView разбивку на страницы, для того чтобы получить в экселевском файле все записи со всех страниц. Так же этот метод определяет имя целевого файла. В моем случае к переданной строковой переменной будет прибавлено текущее время. После этого PrepareAndExport вызывает метод Export, который собственно и генерирует output. PrepareControlForExport нужен для замены пользовательских контролов в GridView, эти контролы нужны на веб-странице, но совершенно бессмысленны в Excel.

    2. Размещаем на asp странице кнопку для осуществления экспорта:

    
    <asp:ImageButton ID="ibExportToExcel" runat="server" 
    
        ImageUrl="~/Img/export-excel.png"
    
        OnClick="ibExportToExcel_Click">
    

    Привязываем в ней onClick событие ibExportToExcel_Click.

    3. Реализуем это событие в code-behind страницы:

    
    Protected Sub ibExportToExcel_Click(ByVal sender As System.Object, 
    
             ByVal e As System.Web.UI.ImageClickEventArgs)
         ExcelExportHelper.PrepareAndExport(Page.Title, gvContent, True)
     End Sub
    

    Ну вот собственно и все, теперь при нажатии на кнопку экспорта произойдет вызов метода PrepareAndExport класса ExcelExportHelper. В качестве параметров этому методу передаем Page.Title (либо любую другую строку в качестве имени файла), GridView, который нужно экспортировать, и true (отображать границы таблицы в экселе).

     

    Теперь несколько “НО”:

    1. Если у клиента установлен Microsoft Excel 2007, то при открытии сгенерированного файла мы получим Security Alert.

    image

    Дело в том, что мы пытаемся подсунуть Excel-ю сгенерированный нами html файл. Он это понимает и сообщает, что содержание файла не соответствует его расширению. К сожалению, нормального пути обойти это сообщение нет. Единственное, что можно сделать – покопаться в реестре:

    [HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Excel\Security]

    “ExtensionHardening”=dword:00000000

       1. Open your Registry (Start -> Run -> regedit.exe)

       2. Navigate to HKEY_CURRENT_USER\SOFTWARE\MICROSOFT\OFFICE\12.0\EXCEL\SECURITY

       3. Right click in the right window and choose New -> DWORD

       4. Type “ExtensionHardening” as the name (without the quotes)

       5. Verify that the data has the value “0?

    2. Если кнопка ibExportToExcel находится в Update Panel, то необходимо добавить ее в триггер:

    <asp:UpdatePanel ID="upContent" runat="server" UpdateMode="Always">
            <Triggers>
                <asp:PostBackTrigger ControlID="ibExportToExcel" />
            </Triggers>