//#define INCLUDE_WEB_FUNCTIONS using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; using LoggingHelper; using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Web.UI.WebControls; namespace ExportToExcel { // // November 2013 // http://www.mikesknowledgebase.com // // Note: if you plan to use this in an ASP.Net application, remember to add a reference to "System.Web", and to uncomment // the "INCLUDE_WEB_FUNCTIONS" definition at the top of this file. // // Release history // - Nov 2013: // Changed "CreateExcelDocument(DataTable dt, string xlsxFilePath)" to remove the DataTable from the DataSet after creating the Excel file. // You can now create an Excel file via a Stream (making it more ASP.Net friendly) // - Jan 2013: Fix: Couldn't open .xlsx files using OLEDB (was missing "WorkbookStylesPart" part) // - Nov 2012: // List<>s with Nullable columns weren't be handled properly. // If a value in a numeric column doesn't have any data, don't write anything to the Excel file (previously, it'd write a '0') // - Jul 2012: Fix: Some worksheets weren't exporting their numeric data properly, causing "Excel found unreadable content in '___.xslx'" errors. // - Mar 2012: Fixed issue, where Microsoft.ACE.OLEDB.12.0 wasn't able to connect to the Excel files created using this class. // public class CreateExcelFile { public static bool CreateExcelDocument(List list, string xlsxFilePath) { DataSet ds = new DataSet(); ds.Tables.Add(ListToDataTable(list)); return CreateExcelDocument(ds, xlsxFilePath); } #region HELPER_FUNCTIONS // This function is adapated from: http://www.codeguru.com/forum/showthread.php?t=450171 // My thanks to Carl Quirion, for making it "nullable-friendly". public static DataTable ListToDataTable(List list) { DataTable dt = new DataTable(); foreach (PropertyInfo info in typeof(T).GetProperties()) { dt.Columns.Add(new DataColumn(info.Name, GetNullableType(info.PropertyType))); } foreach (T t in list) { DataRow row = dt.NewRow(); foreach (PropertyInfo info in typeof(T).GetProperties()) { if (!IsNullableType(info.PropertyType)) row[info.Name] = info.GetValue(t, null); else row[info.Name] = (info.GetValue(t, null) ?? DBNull.Value); } dt.Rows.Add(row); } return dt; } private static Type GetNullableType(Type t) { Type returnType = t; if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { returnType = Nullable.GetUnderlyingType(t); } return returnType; } private static bool IsNullableType(Type type) { return (type == typeof(string) || type.IsArray || (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))); } public static bool CreateExcelDocument(DataTable dt, string xlsxFilePath) { DataSet ds = new DataSet(); ds.Tables.Add(dt); bool result = CreateExcelDocument(ds, xlsxFilePath); ds.Tables.Remove(dt); return result; } #endregion #if INCLUDE_WEB_FUNCTIONS /// /// Create an Excel file, and write it out to a MemoryStream (rather than directly to a file) /// /// DataTable containing the data to be written to the Excel. /// The filename (without a path) to call the new Excel file. /// HttpResponse of the current page. /// True if it was created succesfully, otherwise false. public static bool CreateExcelDocument(DataTable dt, string filename, System.Web.HttpResponse Response) { try { DataSet ds = new DataSet(); ds.Tables.Add(dt); CreateExcelDocumentAsStream(ds, filename, Response); ds.Tables.Remove(dt); return true; } catch (Exception ex) { Trace.WriteLine("Failed, exception thrown: " + ex.Message); return false; } } public static bool CreateExcelDocument(List list, string filename, System.Web.HttpResponse Response) { try { DataSet ds = new DataSet(); ds.Tables.Add(ListToDataTable(list)); CreateExcelDocumentAsStream(ds, filename, Response); return true; } catch (Exception ex) { Trace.WriteLine("Failed, exception thrown: " + ex.Message); return false; } } /// /// Create an Excel file, and write it out to a MemoryStream (rather than directly to a file) /// /// DataSet containing the data to be written to the Excel. /// The filename (without a path) to call the new Excel file. /// HttpResponse of the current page. /// Either a MemoryStream, or NULL if something goes wrong. public static bool CreateExcelDocumentAsStream(DataSet ds, string filename, System.Web.HttpResponse Response) { try { System.IO.MemoryStream stream = new System.IO.MemoryStream(); using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook, true)) { WriteExcelFile(ds, document); } stream.Flush(); stream.Position = 0; Response.ClearContent(); Response.Clear(); Response.Buffer = true; Response.Charset = ""; // NOTE: If you get an "HttpCacheability does not exist" error on the following line, make sure you have // manually added System.Web to this project's References. Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); Response.AddHeader("content-disposition", "attachment; filename=" + filename); Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; byte[] data1 = new byte[stream.Length]; stream.Read(data1, 0, data1.Length); stream.Close(); Response.BinaryWrite(data1); Response.Flush(); Response.End(); return true; } catch (Exception ex) { Trace.WriteLine("Failed, exception thrown: " + ex.Message); return false; } } #endif // End of "INCLUDE_WEB_FUNCTIONS" section /// /// Create an Excel file, and write it to a file. /// /// DataSet containing the data to be written to the Excel. /// Name of file to be written. /// True if successful, false if something went wrong. public static bool CreateExcelDocument(DataSet ds, string excelFilename) { try { using (SpreadsheetDocument document = SpreadsheetDocument.Create(excelFilename, SpreadsheetDocumentType.Workbook)) { WriteExcelFile(ds, document); } Trace.WriteLine("Successfully created: " + excelFilename); return true; } catch (Exception ex) { LoggingUtility objLog = new LoggingUtility(); string path = System.Web.HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["PathLog"]); /// /// Represent string object contain log status /// string errorlogtf = (ConfigurationManager.AppSettings["ErrorLog"]); objLog.ErrorLogFile("ManageEscalation_ExportToExcel", ex.Message, path, errorlogtf); //objLog.AddLogFile(DateTime.Now.ToString(ConfigurationManager.AppSettings["dateTimeFormat"]), path, logtf); Trace.WriteLine("Failed, exception thrown: " + ex.Message); return false; } } private static void WriteExcelFile(DataSet ds, SpreadsheetDocument spreadsheet) { // Create the Excel file contents. This function is used when creating an Excel file either writing // to a file, or writing to a MemoryStream. spreadsheet.AddWorkbookPart(); spreadsheet.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook(); // My thanks to James Miera for the following line of code (which prevents crashes in Excel 2010) spreadsheet.WorkbookPart.Workbook.Append(new BookViews(new WorkbookView())); // If we don't add a "WorkbookStylesPart", OLEDB will refuse to connect to this .xlsx file ! WorkbookStylesPart workbookStylesPart = spreadsheet.WorkbookPart.AddNewPart("rIdStyles"); Stylesheet stylesheet = new Stylesheet(); workbookStylesPart.Stylesheet = stylesheet; // Loop through each of the DataTables in our DataSet, and create a new Excel Worksheet for each. uint worksheetNumber = 1; foreach (DataTable dt in ds.Tables) { // For each worksheet you want to create string workSheetID = "rId" + worksheetNumber.ToString(); string worksheetName = dt.TableName; WorksheetPart newWorksheetPart = spreadsheet.WorkbookPart.AddNewPart(); newWorksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet(); // create sheet data newWorksheetPart.Worksheet.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.SheetData()); // save worksheet WriteDataTableToExcelWorksheet(dt, newWorksheetPart); newWorksheetPart.Worksheet.Save(); // create the worksheet to workbook relation if (worksheetNumber == 1) spreadsheet.WorkbookPart.Workbook.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheets()); spreadsheet.WorkbookPart.Workbook.GetFirstChild().AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheet() { Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart), SheetId = (uint)worksheetNumber, Name = dt.TableName }); worksheetNumber++; } spreadsheet.WorkbookPart.Workbook.Save(); } private static void WriteDataTableToExcelWorksheet(DataTable dt, WorksheetPart worksheetPart) { var worksheet = worksheetPart.Worksheet; var sheetData = worksheet.GetFirstChild(); string cellValue = ""; // Create a Header Row in our Excel file, containing one header for each Column of data in our DataTable. // // We'll also create an array, showing which type each column of data is (Text or Numeric), so when we come to write the actual // cells of data, we'll know if to write Text values or Numeric cell values. int numberOfColumns = dt.Columns.Count; bool[] IsNumericColumn = new bool[numberOfColumns]; string[] excelColumnNames = new string[numberOfColumns]; for (int n = 0; n < numberOfColumns; n++) excelColumnNames[n] = GetExcelColumnName(n); // // Create the Header row in our Excel Worksheet // uint rowIndex = 1; var headerRow = new Row { RowIndex = rowIndex }; // add a row at the top of spreadsheet sheetData.Append(headerRow); for (int colInx = 0; colInx < numberOfColumns; colInx++) { DataColumn col = dt.Columns[colInx]; AppendTextCell(excelColumnNames[colInx] + "1", col.ColumnName, headerRow); IsNumericColumn[colInx] = (col.DataType.FullName == "System.Decimal") || (col.DataType.FullName == "System.Int32"); } // // Now, step through each row of data in our DataTable... // double cellNumericValue = 0; foreach (DataRow dr in dt.Rows) { // ...create a new row, and append a set of this row's data to it. ++rowIndex; var newExcelRow = new Row { RowIndex = rowIndex }; // add a row at the top of spreadsheet sheetData.Append(newExcelRow); for (int colInx = 0; colInx < numberOfColumns; colInx++) { cellValue = dr.ItemArray[colInx].ToString(); // Create cell with data if (IsNumericColumn[colInx]) { // For numeric cells, make sure our input data IS a number, then write it out to the Excel file. // If this numeric value is NULL, then don't write anything to the Excel file. cellNumericValue = 0; if (double.TryParse(cellValue, out cellNumericValue)) { cellValue = cellNumericValue.ToString(); AppendNumericCell(excelColumnNames[colInx] + rowIndex.ToString(), cellValue, newExcelRow); } } else { // For text cells, just write the input data straight out to the Excel file. AppendTextCell(excelColumnNames[colInx] + rowIndex.ToString(), cellValue, newExcelRow); } } } } private static void AppendTextCell(string cellReference, string cellStringValue, Row excelRow) { // Add a new Excel Cell to our Row Cell cell = new Cell() { CellReference = cellReference, DataType = CellValues.String }; CellValue cellValue = new CellValue(); cellValue.Text = cellStringValue; cell.Append(cellValue); excelRow.Append(cell); } private static void AppendNumericCell(string cellReference, string cellStringValue, Row excelRow) { // Add a new Excel Cell to our Row Cell cell = new Cell() { CellReference = cellReference }; CellValue cellValue = new CellValue(); cellValue.Text = cellStringValue; cell.Append(cellValue); excelRow.Append(cell); } private static string GetExcelColumnName(int columnIndex) { // Convert a zero-based column index into an Excel column reference (A, B, C.. Y, Y, AA, AB, AC... AY, AZ, B1, B2..) // // eg GetExcelColumnName(0) should return "A" // GetExcelColumnName(1) should return "B" // GetExcelColumnName(25) should return "Z" // GetExcelColumnName(26) should return "AA" // GetExcelColumnName(27) should return "AB" // ..etc.. // if (columnIndex < 26) return ((char)('A' + columnIndex)).ToString(); char firstChar = (char)('A' + (columnIndex / 26) - 1); char secondChar = (char)('A' + (columnIndex % 26)); return string.Format("{0}{1}", firstChar, secondChar); } } }