Search code examples
asp.netvb.netexceldatatablerendercontrol

Open DataTable in Excel VB


Ok, this is an interesting issue. I have been tasked with modifying an existing VB project. Currently the user selected from a series of dropdowns to select a sql query and then run that query. So the user selects and environment dropdown, the results of that dropdown populates the category dropdown. Once the category is selected, they get a dropdown of available queries. Once they select a query and hit the "Run" button, they get a gridview with the results of the query. Some of the query results are huge. The query I'm running as a test has 40 columns and 20,000 records. The query runs in less than 5 seconds but it takes over a minute to render the gridview. Once the gridview is done rendering, the user has the option to export the results to Excel. And by this, I mean the code opens an instance of Excel through gridview.RenderControl and displays the results in Excel. The user doesn't want to save the excel file and then navigate to the file, they want it to open right from the webform they are using which is what the code does currently.

However, the user doesn't care about the gridview. They don't care if they see it at all. They want to just open Excel. So instead of using gridview.RenderControl, I want to open Excel and populate it with the DataTable (or DataSet) in memory. Any thoughts on the best way to do that?

Here's how they are currently populating the gridview: Dim MyConnection As SqlConnection Dim MyCommand As SqlCommand Dim MyDataTable As DataTable Dim MyReader As SqlDataReader

        MyConnection = New SqlConnection()
        MyConnection.ConnectionString = ConfigurationManager.ConnectionStrings(Connection).ConnectionString

        MyCommand = New SqlCommand()
        MyCommand.CommandText = Sqlquery
        MyCommand.CommandType = CommandType.Text
        MyCommand.Connection = MyConnection

        MyCommand.Connection.Open()
        MyReader = MyCommand.ExecuteReader(CommandBehavior.CloseConnection)

        MyDataTable = New DataTable()
        MyDataTable.Load(MyReader)

        If (MyDataTable.Rows.Count > 0) Then
            QueryresultPanel.Visible = True
            gvLineItems.DataSource = MyDataTable
            gvLineItems.DataBind()
        End If

        MyDataTable.Dispose()
        MyCommand.Dispose()
        MyConnection.Dispose()

Here's how they're opening and populating the instance of Excel:

     Protected Sub btnExportToExcel_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnExportToExcel.Click
        Response.Clear()
        Response.Buffer = True
        '
        ' Set the content type to Excel.
        ' 
        Response.AddHeader("content-disposition", "attachment;filename=GridViewExport.xls")
        Response.Charset = ""
        Response.ContentType = "application/vnd.ms-excel"
        ' 
        ' Turn off the view state.
        ' 
        Me.EnableViewState = False
        Dim oStringWriter As New System.IO.StringWriter()
        Dim oHtmlTextWriter As New System.Web.UI.HtmlTextWriter(oStringWriter)
        ' 
        ' Get the HTML for the control.
        '
        gvLineItems.RenderControl(oHtmlTextWriter)
        ' 
        ' Write the HTML back to the browser.
        '
        Response.Write(oStringWriter.ToString())
        Response.[End]()
    End Sub

Obviously, there's no RenderControl for a DataTable or DataSet and can't figure out how to get this record set to render in an instance of Excel without saving it to a file first.


Solution

  • Alright, here's the solution I found (in case anyone is interested). It's pretty simple actually. I just looped through the datatable and used StringWriter.

        Protected Sub WriteToExcelFile(dt As DataTable)
        Dim sw As StringWriter
    
        For Each datacol As DataColumn In dt.Columns
            sw.Write(datacol.ColumnName + vbTab)
        Next
    
        Dim row As DataRow
        For Each row In dt.Rows
            sw.Write(vbNewLine)
            Dim column As DataColumn
            For Each column In dt.Columns
                If Not row(column.ColumnName) Is Nothing Then
                    sw.Write(row(column).ToString() + vbTab)
                Else
                    sw.Write(String.Empty + vbTab)
                End If
            Next column
        Next row
    
        Response.Clear()
        Response.ContentType = "application/vnd.ms-excel"
        Response.AddHeader("Content-Disposition", "attachment;filename=DataTable.xls")
        Response.Output.Write(sw.ToString())
        Response.Flush()
        System.Web.HttpContext.Current.Response.Flush()
        System.Web.HttpContext.Current.Response.SuppressContent = True
        System.Web.HttpContext.Current.ApplicationInstance.CompleteRequest()
    End Sub