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
Old thread - but thought i would throw my code in here. I wrote a little function to write a data table to a new excel sheet at a specified path (location). Also you will need to add a reference to microsoft excel 14.0 library.
I pulled from this thread on writing anything to excel - How to write some data to excel file(.xlsx)
i used that to extrapolate how to write a datatable
*note in catch statements i have an errorhandler static class reference (you can ignore those)
using excel = Microsoft.Office.Interop.Excel;
using System.IO;
using System.Data;
using System.Runtime.InteropServices;
//class and namespace wrapper is not shown in this example
private void WriteToExcel(System.Data.DataTable dt, string location)
{
//instantiate excel objects (application, workbook, worksheets)
excel.Application XlObj = new excel.Application();
XlObj.Visible = false;
excel._Workbook WbObj = (excel.Workbook)(XlObj.Workbooks.Add(""));
excel._Worksheet WsObj = (excel.Worksheet)WbObj.ActiveSheet;
//run through datatable and assign cells to values of datatable
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(location);
}
catch (COMException x)
{
ErrorHandler.Handle(x);
}
catch (Exception ex)
{
ErrorHandler.Handle(ex);
}
finally
{
WbObj.Close();
}
}
To export data to Excel, you can use the ClosedXML.Report library (https://github.com/ClosedXML/ClosedXML.Report). Believe me, this is a wonderful library and easy for her to use. The library does not need Excel Interop. ClosedXML.Report generates an Excel file based on a template that you can create in Excel using any formatting. For example:
var template = new XLTemplate(@".\Templates\report.xlsx");
using (var db = new DbDemos())
{
var cust = db.customers.LoadWith(c => c.Orders).First();
template.AddVariable(cust);
template.Generate();
}
template.SaveAs(outputFile);
Just Make use of the CloseMXL.Excel Library. It's easy and pretty fast too.
Class
private DataTable getAllList()
{
string constr = ConfigurationManager.ConnectionStrings["RConnection"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand("SELECT EmpId, gender, EmpName, pOnHold FROM Employee WHERE EmpId= '"+ AnyVariable + "' ORDER BY EmpName"))
{
using (SqlDataAdapter da = new SqlDataAdapter())
{
DataTable dt = new DataTable();
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
da.SelectCommand = cmd;
da.Fill(dt);
dt.Columns[0].ColumnName = "Employee Id";
dt.Columns[1].ColumnName = "Gender";
dt.Columns[2].ColumnName = "Employee Name";
dt.Columns[3].ColumnName = "On Hold";
return dt;
}
}
}
}
Then another method which get the Dataset
public DataSet getDataSetExportToExcel()
{
DataSet ds = new DataSet();
DataTable dtEmp = new DataTable("CLOT List");
dtEmp = getAllList();
ds.Tables.Add(dtEmp);
ds.Tables[0].TableName = "Employee"; //If you which to use Mutliple Tabs
return ds;
}
Now you Button Click Event
protected void btn_Export_Click(object sender, EventArgs e)
{
DataSet ds = getDataSetExportToExcel();
using (XLWorkbook wb = new XLWorkbook())
{
wb.Worksheets.Add(ds);
wb.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
wb.Style.Font.Bold = true;
Response.Clear();
Response.Buffer = true;
Response.Charset = "";
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment;filename=EmployeeonHoldList.xlsx");
using (MemoryStream MyMemoryStream = new MemoryStream())
{
wb.SaveAs(MyMemoryStream);
MyMemoryStream.WriteTo(Response.OutputStream);
Response.Flush();
Response.End();
}
}
}
I would recommend ClosedXML -
You can turn a DataTable into an Excel worksheet with some very readable code:
XLWorkbook wb = new XLWorkbook();
DataTable dt = GetDataTableOrWhatever();
wb.Worksheets.Add(dt,"WorksheetName");
The developer is responsive and helpful. The project is actively developed, and the documentation is superb.
Solution based on tuncalik (thanks for idea) article, but in case of big tables is working much more faster (and is a little less clear).
public static class My_DataTable_Extensions
{
/// <summary>
/// Export DataTable to Excel file
/// </summary>
/// <param name="DataTable">Source DataTable</param>
/// <param name="ExcelFilePath">Path to result file name</param>
public static void ExportToExcel(this System.Data.DataTable DataTable, string ExcelFilePath = null)
{
try
{
int ColumnsCount;
if (DataTable == null || (ColumnsCount = DataTable.Columns.Count) == 0)
throw new Exception("ExportToExcel: Null or empty input table!\n");
// load excel, and create a new workbook
Microsoft.Office.Interop.Excel.Application Excel = new Microsoft.Office.Interop.Excel.Application();
Excel.Workbooks.Add();
// single worksheet
Microsoft.Office.Interop.Excel._Worksheet Worksheet = Excel.ActiveSheet;
object[] Header = new object[ColumnsCount];
// column headings
for (int i = 0; i < ColumnsCount; i++)
Header[i] = DataTable.Columns[i].ColumnName;
Microsoft.Office.Interop.Excel.Range HeaderRange = Worksheet.get_Range((Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[1, 1]), (Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[1, ColumnsCount]));
HeaderRange.Value = Header;
HeaderRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
HeaderRange.Font.Bold = true;
// DataCells
int RowsCount = DataTable.Rows.Count;
object[,] Cells = new object[RowsCount, ColumnsCount];
for (int j = 0; j < RowsCount; j++)
for (int i = 0; i < ColumnsCount; i++)
Cells[j, i] = DataTable.Rows[j][i];
Worksheet.get_Range((Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[2, 1]), (Microsoft.Office.Interop.Excel.Range)(Worksheet.Cells[RowsCount + 1, ColumnsCount])).Value = Cells;
// check fielpath
if (ExcelFilePath != null && ExcelFilePath != "")
{
try
{
Worksheet.SaveAs(ExcelFilePath);
Excel.Quit();
System.Windows.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 filepath is given
{
Excel.Visible = true;
}
}
catch (Exception ex)
{
throw new Exception("ExportToExcel: \n" + ex.Message);
}
}
}
I wanted to add this answer because I spent a great deal of time looking for a fast, reliable method to do this and no complete examples of using OpenXMLWriter for this purpose existed anywhere that I could find.
First, COM/Interop (which many of the other answers use) is OK for this purpose, but it suffers from some sensitivities. I've used it for decades and it's mostly stable, but when implementing a data warehouse front-end for hundreds of users, I found it to be subject to too many issues depending on the machine and what the user did, so I switched to OpenXML. OpenXML DOM is fairly good for this purpose, but it's slower than using OpenXMLWriter. When you get into large datasets (100K+) with lots of columns, DOM is much slower than OpenXMLWriter, so I use the latter. The method below writes 420K+ rows with 30+ fields in less than 30 seconds.
I hope the comments are sufficient to guide anyone through what it's doing. It is simplified, in that it writes all values to the file as strings, but you can implement logic to write various datatypes (and use various cell formats) based on the content of your data. You can also adapt this for use on a DataGridView (instead of a DataTable) by changing just a few things (namely the loops through columns/rows).
A reference to DocumentFormat.OpenXML (d/l with the OpenXML SDK) and WindowsBase is required.
Imports DocumentFormat.OpenXml
Imports DocumentFormat.OpenXml.Spreadsheet
Imports DocumentFormat.OpenXml.Packaging
Public Sub ExportToExcelXML(ByRef dt As DataTable, filename As String)
Dim wbp As WorkbookPart, wsp As WorksheetPart
'If this DataTable has more rows in it than can fit in Excel, throw an exception
If dt.Rows.Count > 1048575 Then Throw New Exception("The DataTable is too large to export to Excel.")
'Delete any previous file of the same name that may exist.
File.Delete(filename)
'Create an OpenXML SpreadsheetDocument...
Using xls = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook)
'Add a WorkbookPart to the Spreadsheet Doc, then add a WorksheetPart to the WorkbookPart.
wbp = xls.AddWorkbookPart()
wsp = wbp.AddNewPart(Of WorksheetPart)
'Now we need to add the "StyleSheet" to the WorkbookPart (that we just added above). This will allow us to apply formatting to our Cells.
'Add the WbStylesPart and the StyleSheet.
Dim stp As WorkbookStylesPart = wbp.AddNewPart(Of WorkbookStylesPart)
Dim ss As New Stylesheet
'Create the only two Fonts we're going to use (Regular and Bold).
Dim fBold As New Font
fBold.Append(New Bold)
Dim fnts As New Fonts
fnts.Append(New Font) 'This creates the default (unmodified, regular) Font. It's added first, so its index is 0.
fnts.Append(fBold) 'This creates the Bold font. It's added second, so its index is 1.
'Create the default Fill/Border settings (these have to be here, even though I don't set any custom fills/borders).
Dim flls As New Fills
Dim brdrs As New Borders
flls.Append(New Fill)
brdrs.Append(New Border)
'Now I have to add formats (NumberFormat and CellFormat). First, you create a NumberFormat. This is basically the pattern of
' the format (i.e. "@" for Text). For now, I only need a Text format, but I can add more patterns if needed.
' I give the format an ID of 164, since 163 is where the built-in Excel formats end.
Dim nbrfmts As New NumberingFormats
nbrfmts.Append(New NumberingFormat With {.NumberFormatId = 164, .FormatCode = "@"})
'Create the first two CellFormats: Default, which will have an index of 0 and "Header" (Bold/Centered) with an index of 1.
Dim cellfmts As New CellFormats()
cellfmts.Append(New CellFormat() With {.FontId = 0, .NumberFormatId = 164, .FillId = 0, .BorderId = 0})
cellfmts.Append(New CellFormat() With {.FontId = 1, .NumberFormatId = 164,
.Alignment = New Alignment() With {.WrapText = True, .Horizontal = HorizontalAlignmentValues.Center}})
'Add all of the Fonts/Fills/Borders/etc to the StyleSheet and add it all to the WorkbookStylesPart.
ss.Append(fnts)
ss.Append(flls)
ss.Append(brdrs)
ss.Append(cellfmts)
ss.NumberingFormats = nbrfmts
stp.Stylesheet = ss
stp.Stylesheet.Save()
'Now create an OpenXMLWriter using the WorksheetPart to write the cells to the worksheet.
Using oxw As OpenXmlWriter = OpenXmlWriter.Create(wsp)
'Write the start element for the Worksheet and the Columns...
oxw.WriteStartElement(New Worksheet)
oxw.WriteStartElement(New Columns())
'Now I'm going to loop through the columns in the DataTable...
For c As Integer = 0 To dt.Columns.Count - 1
'Now we'll get the width for the column. To do this, we loop through all of the rows and measure the width of the text
' using the default Excel Font (currently Font: Calibri Size: 11) and return the largest width (in pixels) to use below.
' Why not do this loop below (when I loop through the rows to write the Cells)? Because you can't. You have to
' write the Column XML first before writing the SheetData/Row/Cell XML (I confirmed this by trying it), so there's
' no way (that I'm aware of) to avoid looping through all of the rows twice if you want to AutoFit.
'Setup vars we'll use for getting the column widths (below).
Dim g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero)
Dim fnt = New System.Drawing.Font("Calibri", 11)
Dim wid As Double = 0
'Get the width of the header (because if this is wider than the widest value, we'll use the header text's width).
' I found that adding 2 pixels to the width was necessary to get the column as wide as Excel would make it.
Dim tmp As Double = g.MeasureString(dt.Columns(c).ColumnName, New System.Drawing.Font(fnt, System.Drawing.FontStyle.Bold)).Width + 2
'Loop through the rows in the dt and get the width of the value in that row/col. If it's wider than the widest
' width we've encountered thus far, use the new wider width as our basis.
For Each row As DataRow In dt.Rows
If tmp > wid Then wid = tmp
tmp = g.MeasureString(row(c).ToString, fnt).Width
Next
'Set the column attributes and write it to the file. The Width is set using a formula that converts from pixels to Excel's column width values.
Dim oxa As New List(Of OpenXmlAttribute) From {New OpenXmlAttribute("min", Nothing, c + 1), New OpenXmlAttribute("max", Nothing, c + 1),
New OpenXmlAttribute("width", Nothing, System.Math.Round((wid - 12 + 5) / 7D + 1, 2))}
oxw.WriteStartElement(New Column(), oxa)
oxw.WriteEndElement()
Next
'CLose out the Columns collection.
oxw.WriteEndElement()
'Write the start element for the SheetData...
oxw.WriteStartElement(New SheetData)
'Write the start element for the Header row.
oxw.WriteStartElement(New Row)
'Loop through the Columns in the dt.
For Each col As DataColumn In dt.Columns
'Write a cell for this column's Header. All Header cells are written with a DataType of String ("str").
' I ALSO apply the "Header" CellFormat (StyleIndex 1) to all of the Header Cells. This makes them Bold and Centered.
WriteCell(oxw, col.ColumnName, "str", 1)
Next
'Close out the Header row.
oxw.WriteEndElement()
'Loop through all of the rows in the dt...
For Each row As DataRow In dt.Rows
'Write a StartElement for this row...
oxw.WriteStartElement(New Row)
'Loop through all of the columns in the dt...
For c As Integer = 0 To dt.Columns.Count - 1
'Write a value in this row/column to the Excel file. I use the datatype of "String" and the default CellFormat/StyleIndex.
WriteCell(oxw, row(c).ToString, "str", 0)
Next
'Close out this row.
oxw.WriteEndElement()
Next
'Close out the Worksheet and SheetData elements...
oxw.WriteEndElement()
oxw.WriteEndElement()
End Using
'Now we're going to create an OpenXMLWriter using the WorkbookPart (that we created above)...
Using oxw As OpenXmlWriter = OpenXmlWriter.Create(wbp)
'Add starting elements for the Workbook and Sheets collection.
oxw.WriteStartElement(New Workbook())
oxw.WriteStartElement(New Sheets())
'Add the Sheet (name the Sheet after the file name minus the extension).
oxw.WriteElement(New Sheet() With {.Name = Path.GetFileNameWithoutExtension(filename), .SheetId = 1, .Id = xls.WorkbookPart.GetIdOfPart(wsp)})
'Write End elements for the Workbook/Sheets
oxw.WriteEndElement()
oxw.WriteEndElement()
End Using
End Using
End Sub
'This Sub is used to write a value to a Cell using OpenXMLWriter.
Private Sub WriteCell(ByRef oxw As OpenXmlWriter, value As String, datatype As String, style As UInt32Value)
Dim oxa As New List(Of OpenXmlAttribute) From {New OpenXmlAttribute("t", Nothing, datatype), New OpenXmlAttribute("s", Nothing, style)}
oxw.WriteStartElement(New Cell(), oxa)
If value <> Nothing Then oxw.WriteElement(New CellValue(value))
oxw.WriteEndElement()
End Sub