How to export DataTable to Excel

后端 未结 21 2325
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-22 15:36

How can I export a DataTable to Excel in C#? I am using Windows Forms. The DataTable is associated with a DataGridView control. I have

相关标签:
21条回答
  • 2020-11-22 15:53
    Private tmr As System.Windows.Forms.Timer
    
    Private Sub TestExcel() Handles Button1.Click
    
        '// Initial data: SQL Server table with 6 columns and 293000 rows.
    
    
        '// Data table holding all data
        Dim dt As New DataTable("F161")
    
        '// Create connection
        Dim conn As New SqlConnection("Server=MYSERVER;Database=Test;Trusted_Connection=Yes;")
        Dim fAdapter As New SqlDataAdapter With
        {
            .SelectCommand = New SqlCommand($"SELECT * FROM dbo.MyTable", conn)
        }
    
        '// Fill DataTable
        fAdapter.Fill(dt)
    
        '// Create Excel application
        Dim xlApp As New Excel.Application With {.Visible = True}
    
        '// Temporarily disable screen updating
        xlApp.ScreenUpdating = False
    
        '// Create brand new workbook
        Dim xlBook As Excel.Workbook = xlApp.Workbooks.Add()
        Dim xlSheet As Excel.Worksheet = DirectCast(xlBook.Sheets(1), Excel.Worksheet)
    
        '// Get number of rows
        Dim rows_count = dt.Rows.Count
        '// Get number of columns
        Dim cols_count = dt.Columns.Count
    
        '// Here 's the core idea: after receiving data
        '// you need to create an array and transfer it to sheet.
        '// Why array?
        '// Because it's the fastest way to transfer data to Excel's sheet.
        '// So, we have two tasks:
        '// 1) Create array
        '// 2) Transfer array to sheet
    
        '// =========================================================
        '// TASK 1: Create array
        '// =========================================================
        '// In order to create array, we need to know that
        '// Excel's Range object expects 2-D array whose lower bounds
        '// of both dimensions start from 1.
        '// This means you can't use C# array.
        '// You need to manually create such array.
        '// Since we already calculated number of rows and columns,
        '// we can use these numbers in creating array.
        Dim arr = Array.CreateInstance(GetType(Object), {rows_count, cols_count}, {1, 1})
    
        '// Fill array
        For r = 0 To rows_count - 1
            For c = 0 To cols_count - 1
                arr(r + 1, c + 1) = dt.Rows(r)(c)
            Next
        Next
    
        '// =========================================================
        '// TASK 2: Transfer array to sheet
        '// =========================================================
        '// Now we need to transfer array to sheet.
        '// So, how transfer array to sheet fast?
        '// 
        '// THE FASTEST WAY TO TRANSFER DATA TO SHEET IS TO ASSIGN ARRAY TO RANGE.
        '// We could, of course, hard-code values, but Resize property
        '// makes this work a breeze:
        xlSheet.Range("A1").Resize.Resize(rows_count, cols_count).Value = arr
    
        '// If we decide to dump data by iterating over array,
        '// it will take LOTS of time.
        '// For r = 1 To rows_count
        '//     For c = 1 To cols_count
        '//         xlSheet.Cells(r, c) = arr(r, c)
        '//     Next
        '// Next
    
        '// Here are time results:
        '// 1) Assigning array to Range: 3 seconds
        '// 2) Iterating over array: 45 minutes
    
        '// Turn updating on
        xlApp.ScreenUpdating = True
        xlApp = Nothing
        xlBook = Nothing
        xlSheet = Nothing
    
        '// Here we have another problem:
        '// creating array took lots of memory (about 150 MB).
        '// Using 'GC.Collect()', by unknown reason, doesn't help here.
        '// However, if you run GC.Collect() AFTER this procedure is finished
        '// (say, by pressing another button and calling another procedure),
        '// then the memory is cleaned up.
        '// I was wondering how to avoid creating some extra button to just release memory,
        '// so I came up with the idea to use timer to call GC.
        '// After 2 seconds GC collects all generations.
        '// Do not forget to dispose timer since we need it only once.
    
        tmr = New Timer()
        AddHandler tmr.Tick,
            Sub()
                GC.Collect()
                GC.WaitForPendingFinalizers()
                GC.WaitForFullGCComplete()
                tmr.Dispose()
            End Sub
        tmr.Interval = TimeSpan.FromSeconds(2).TotalMilliseconds()
        tmr.Start()
    
    End Sub
    
    0 讨论(0)
  • 2020-11-22 15:54

    An elegant option is writing an extension method (see below) for the DataTable class of .net framework.

    This extention method can be called as follows:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Excel = Microsoft.Office.Interop.Excel;
    using System.Data;
    using System.Data.OleDb;
    
    DataTable dt;
    // fill table data in dt here 
    ...
    
    // export DataTable to excel
    // save excel file without ever making it visible if filepath is given
    // don't save excel file, just make it visible if no filepath is given
    dt.ExportToExcel(ExcelFilePath);
    

    Extension method for DataTable class:

    public static class My_DataTable_Extensions
    {
    
        // Export DataTable into an excel file with field names in the header line
        // - Save excel file without ever making it visible if filepath is given
        // - Don't save excel file, just make it visible if no filepath is given
        public static void ExportToExcel(this DataTable tbl, string excelFilePath = null) {
            try {
                if (tbl == null || tbl.Columns.Count == 0)
                    throw new Exception("ExportToExcel: Null or empty input table!\n");
    
                // load excel, and create a new workbook
                var excelApp = new Excel.Application();
                excelApp.Workbooks.Add();
    
                // single worksheet
                Excel._Worksheet workSheet = excelApp.ActiveSheet;
    
                // column headings
                for (var i = 0; i < tbl.Columns.Count; i++) {
                    workSheet.Cells[1, i + 1] = tbl.Columns[i].ColumnName;
                }
    
                // rows
                for (var i = 0; i < tbl.Rows.Count; i++) {
                    // to do: format datetime values before printing
                    for (var j = 0; j < tbl.Columns.Count; j++) {
                        workSheet.Cells[i + 2, j + 1] = tbl.Rows[i][j];
                    }
                }
    
                // check file path
                if (!string.IsNullOrEmpty(excelFilePath)) {
                    try {
                        workSheet.SaveAs(excelFilePath);
                        excelApp.Quit();
                        MessageBox.Show("Excel file saved!");
                    }
                    catch (Exception ex) {
                        throw new Exception("ExportToExcel: Excel file could not be saved! Check filepath.\n"
                                            + ex.Message);
                    }
                } else { // no file path is given
                    excelApp.Visible = true;
                }
            }
            catch (Exception ex) {
                throw new Exception("ExportToExcel: \n" + ex.Message);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 15:55

    use the following class

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data;
    using excel = Microsoft.Office.Interop.Excel;
    using EL = ExcelLibrary.SpreadSheet;
    using System.Drawing;
    using System.Collections;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    
    namespace _basic
    {
    public class ExcelProcesser
    {
        public void WriteToExcel(System.Data.DataTable dt)
        {
            excel.Application XlObj = new excel.Application();
            XlObj.Visible = false;
            excel._Workbook WbObj = (excel.Workbook)(XlObj.Workbooks.Add(""));
            excel._Worksheet WsObj = (excel.Worksheet)WbObj.ActiveSheet;
            object misValue = System.Reflection.Missing.Value;
    
    
            try
            {
                int row = 1; int col = 1;
                foreach (DataColumn column in dt.Columns)
                {
                    //adding columns
                    WsObj.Cells[row, col] = column.ColumnName;
                    col++;
                }
                //reset column and row variables
                col = 1;
                row++;
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    //adding data
                    foreach (var cell in dt.Rows[i].ItemArray)
                    {
                        WsObj.Cells[row, col] = cell;
                        col++;
                    }
                    col = 1;
                    row++;
                }
                WbObj.SaveAs(fileFullName, excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                WbObj.Close(true, misValue, misValue);
            }
        }
    }
    

    }

    0 讨论(0)
  • 2020-11-22 15:58

    In regards tuncalik's answer, which is great, especially if you want to have a little play with the code :) but it is putting my dates into Excel in American format i.e. 2nd March 2014 in the UK is 02/03/2014 but in the USA its 03/02/2014 with month 1st, then day of week after. I need to have it in UK format, any ideas please?

    I have checked it is stored in UK format in my DataTable and also my Excel is set to UK but for some reason when it makes the Excel document it thinks its USA (is this because Microsoft are a USA company :)

    I'll try experimenting with culture codes but not sure where to put that yet. Tried but this had no effect.

    p.s.

    I did have to change one line to get it to work by adding a 'cast' as below

    // single worksheet
    Excel._Worksheet workSheet = (Excel._Worksheet)excelApp.ActiveSheet;
    

    Update: I have achieved UK formatting of the dates by converting to LongDateTime format, its only a work around though.

    DateTime startDate = Convert.ToDateTime(myList[0].ToString());
    string strStartDate = startDate.ToLongDateString();
    DateTime endDate = Convert.ToDateTime(myList[myListTotalRows].ToString());
    string strEndDate = endDate.ToLongDateString();    
    

    cheers.

    0 讨论(0)
  • 2020-11-22 15:59

    The best and easiest way

    private void exportToExcel(DataTable dt)
        {
    
            /*Set up work book, work sheets, and excel application*/
            Microsoft.Office.Interop.Excel.Application oexcel = new Microsoft.Office.Interop.Excel.Application();
            try
            {
                string path = AppDomain.CurrentDomain.BaseDirectory;
                object misValue = System.Reflection.Missing.Value;
                Microsoft.Office.Interop.Excel.Workbook obook = oexcel.Workbooks.Add(misValue);
                Microsoft.Office.Interop.Excel.Worksheet osheet = new Microsoft.Office.Interop.Excel.Worksheet();
    
    
              //  obook.Worksheets.Add(misValue);
    
                osheet = (Microsoft.Office.Interop.Excel.Worksheet)obook.Sheets["Sheet1"];
                int colIndex = 0;
                int rowIndex = 1;
    
                foreach (DataColumn dc in dt.Columns)
                {
                    colIndex++;
                    osheet.Cells[1, colIndex] = dc.ColumnName;
                }
                foreach (DataRow dr in dt.Rows)
                {
                    rowIndex++;
                    colIndex = 0;
    
                    foreach (DataColumn dc in dt.Columns)
                    {
                        colIndex++;
                        osheet.Cells[rowIndex, colIndex] = dr[dc.ColumnName];
                    }
                }
    
                osheet.Columns.AutoFit();
                string filepath = "C:\\Temp\\Book1";
    
                //Release and terminate excel
    
                obook.SaveAs(filepath);
                obook.Close();
                oexcel.Quit();
                releaseObject(osheet);
    
                releaseObject(obook);
    
                releaseObject(oexcel);
                GC.Collect();
            }
            catch (Exception ex)
            {
                oexcel.Quit();
                log.AddToErrorLog(ex, this.Name);
            }
        }
    
    0 讨论(0)
  • 2020-11-22 16:02

    Using DocumentFormat.OpenXml nuget package, I have created a singleton class which handles export to excel from either a DataTable or DataSet. Tables will be represented as separate sheets in a workbook.

    The main class

    public sealed class OfficeOpenXML
        {
            private static Lazy<OfficeOpenXML> _instance = new Lazy<OfficeOpenXML>(() => new OfficeOpenXML());
            private OfficeOpenXML()
            {
    
            }
            public static OfficeOpenXML GetInstance()
            {
                return _instance.Value;
            }
    
            public MemoryStream GetExcelStream(DataSet ds, bool firstRowAsHeader = false)
            {
                if (ds == null || ds.Tables.Count == 0)
                {
                    return null;
                }
    
                MemoryStream stream = new MemoryStream();
                using (var excel = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook))
                {
                    //create doc and workbook
                    WorkbookPart workbookPart = excel.AddWorkbookPart();
                    Workbook workbook = new Workbook();
                    Sheets sheets = new Sheets();
                    //loop all tables in the dataset
                    for (int iTable = 0; iTable < ds.Tables.Count; iTable++)
                    {
                        var table = ds.Tables[iTable];
                        //create sheet part
                        WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
                        Worksheet worksheet = new Worksheet();
                        SheetData data = new SheetData();
                        List<Row> allRows = new List<Row>();
    
                        //setting header of the sheet
                        Row headerRow = new Row() { RowIndex = 1 };
                        for (int iColumn = 0; iColumn < table.Columns.Count; iColumn++)
                        {
                            var col = table.Columns[iColumn];
                            //if first row of table is not the header then set columns of table as header of sheet
                            if (!firstRowAsHeader)
                            {
                                headerRow.Append(new Cell
                                {
                                    DataType = CellValues.String,
                                    CellValue = new CellValue(col.ColumnName)
                                });
                            }
                            else
                            {
                                headerRow.Append(new Cell
                                {
                                    DataType = CellValues.String,
                                    CellValue = new CellValue(Convert.ToString(table.Rows[0][col]))
                                });
                            }
                        }
                        allRows.Add(headerRow);
    
                        //setting other data rows
                        if (table.Rows != null && table.Rows.Count != 0)
                        {
                            for (int iRow = firstRowAsHeader ? 1 : 0; iRow < table.Rows.Count; iRow++)
                            {
                                var row = table.Rows[iRow];
                                Row valueRow = new Row { RowIndex = (uint)(iRow + (firstRowAsHeader ? 1 : 2)) };
    
                                for (int iColumn = 0; iColumn < table.Columns.Count; iColumn++)
                                {
                                    var col = table.Columns[iColumn];
                                    valueRow.Append(new Cell
                                    {
                                        DataType = Format(col.DataType),
                                        CellValue = new CellValue(Convert.ToString(row[col]))
                                    });
                                }
                                allRows.Add(valueRow);
                            }
                        }
    
                        //add rows to the data
                        data.Append(allRows);
                        worksheet.Append(data);
                        worksheetPart.Worksheet = worksheet;
                        worksheetPart.Worksheet.Save();
    
                        //add worksheet to main sheets
                        sheets.Append(new Sheet
                        {
                            Name = string.IsNullOrWhiteSpace(table.TableName) ? "Sheet" + (iTable + 1) : table.TableName,
                            Id = workbookPart.GetIdOfPart(worksheetPart),
                            SheetId = (uint)iTable + 1
                        });
                    }//single table processing ends here
    
                    //add created sheets to workbook
                    workbook.Append(sheets);
                    excel.WorkbookPart.Workbook = workbook;
                    excel.WorkbookPart.Workbook.Save();
    
    
                    excel.Close();
                }
                stream.Seek(0, SeekOrigin.Begin);    
                return stream;
    
    
            }
            public MemoryStream GetExcelStream(DataTable dt, bool firstRowAsHeader = false)
            {
                DataSet ds = new DataSet();
                ds.Tables.Add(dt);
                return GetExcelStream(ds, firstRowAsHeader);
            }
    
    
    
            #region Excel Helpers
    
            CellValues Format(Type t)
            {
    
                switch (t.ToString())
                {
    
                    case "System.String":
                        return CellValues.String;
                    case "System.DateTime":
                        return CellValues.Date;
                    case "System.Boolean":
                        return CellValues.Boolean;
                    case "System.Int16":
                        return CellValues.Number;
                    case "System.Int32":
                        return CellValues.Number;
                    case "System.Int64":
                        return CellValues.Number;
                    case "System.UInt16":
                        return CellValues.Number;
                    case "System.UInt32":
                        return CellValues.Number;
                    case "System.UInt64":
                        return CellValues.Number;
                    case "System.Decimal":
                        return CellValues.Number;
                    case "System.Double":
                        return CellValues.Number;
                    case "System.Single":
                        return CellValues.Number;
                    default:
                        return CellValues.String;
                }
            }
            #endregion
        }
    

    Save to a file

            var excelApp = OfficeOpenXML.GetInstance();
            var dt = GetDataTableFromDB();
            using (var stream = excelApp.GetExcelStream(dt, false))//use true to hide datatable columns from excel
            {
    
                using (FileStream fs = new FileStream(@"C:\Users\Public\myexcel.xlsx", FileMode.Create))
                {
                    stream.CopyTo(fs);
                    fs.Flush();
                }
            }
    

    Download in an MVC application

    public ActionResult DownloadReport()
    {
        var ds = GetDataSetFromDB();
        var excelApp = OfficeOpenXML.GetInstance();
        var file = excelApp.GetExcelStream(ds, false);
        return File(file, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Guid.NewGuid().ToString() + ".xlsx");
    }
    
    0 讨论(0)
提交回复
热议问题