Как я могу экспортировать DataTableв Excel на C #? Я использую Windows Forms. DataTableСвязан с DataGridViewконтролем. Мне нужно экспортировать записи DataTableв Excel.
Самый простой способ - выполнить вложенный цикл foreach для элементов и подэлементов.
Саид Яздани
ПРИМЕЧАНИЕ. Если вы пытаетесь передать значения из таблицы данных в объект, а затем в Excel, вы также должны выполнять обработку ошибок типа данных. Например, Guids уничтожит ваше назначение с исключением HRESULT: 0x800A03EC. Один способ обхода без тестирования типов данных - использовать «ToString ()» при заполнении вашего объекта. Excel самостоятельно преобразует числа обратно в числовой формат. FlashTrev as решает связанный с этим вопрос даты / времени.
Добавление ~ 6 МБ библиотек, на которые есть ссылки, не сделало бы приложение немного тяжелым?
ʞᴉɯ
4
Хороший вопрос @MicheleVirgilio. Я не проводил никаких тестов для количественной оценки воздействия. Но чего бы это ни стоило, меня это не беспокоило ни в одном из проектов, которые я использовал, на самом деле я не могу сказать, что когда-либо замечал это.
hmqcnoesy
Этот код вернул мне Excel с одним столбцом со значениемClosedXML.Excel.XLWorkbook
это ловушка
79
Попробуйте простой код, чтобы преобразовать DataTable в файл Excel как csv:
Какой отличный ответ, чувак. У меня нет возможности проголосовать за ваш ответ более одного раза, иначе я мог бы дать даже более 100 голосов.
Ашок Кумар
2
@Cuong Le - Если в ячейке две запятые, тогда будет проблема с "string.Join (", ")"
suneel ranga
@Cuong Le, где будет "excel.csv"место?
Jogi
2
@suneelranga - Если ячейка (например, в row.ItemArray) содержит ,(запятую), то по стандарту CSV эта ячейка должна быть заключена в кавычки, ","а затем отображаться в файле как обычно. Итак, да - это вызовет проблему, потому что этот код не обнаруживает ,и не применяет кавычки.
Tom Leys
1
@ Si8 после сохранения вы можете сделать Process.Start (Your File), и он откроет его для них. Я думаю, это настолько близко, насколько вы можете понять ..
TimmRH
40
Элегантный вариант - написать метод расширения (см. Ниже) для класса DataTable инфраструктуры .NET.
Этот метод расширения можно назвать следующим образом:
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);
Метод расширения для класса DataTable:
publicstaticclassMy_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 givenpublicstaticvoidExportToExcel(thisDataTable tbl,string excelFilePath =null){try{if(tbl ==null|| tbl.Columns.Count==0)thrownewException("ExportToExcel: Null or empty input table!\n");// load excel, and create a new workbookvar excelApp =newExcel.Application();
excelApp.Workbooks.Add();// single worksheetExcel._Worksheet workSheet = excelApp.ActiveSheet;// column headingsfor(var i =0; i < tbl.Columns.Count; i++){
workSheet.Cells[1, i +1]= tbl.Columns[i].ColumnName;}// rowsfor(var i =0; i < tbl.Rows.Count; i++){// to do: format datetime values before printingfor(var j =0; j < tbl.Columns.Count; j++){
workSheet.Cells[i +2, j +1]= tbl.Rows[i][j];}}// check file pathif(!string.IsNullOrEmpty(excelFilePath)){try{
workSheet.SaveAs(excelFilePath);
excelApp.Quit();MessageBox.Show("Excel file saved!");}catch(Exception ex){thrownewException("ExportToExcel: Excel file could not be saved! Check filepath.\n"+ ex.Message);}}else{// no file path is given
excelApp.Visible=true;}}catch(Exception ex){thrownewException("ExportToExcel: \n"+ ex.Message);}}}
@ alex.pulver он также не работает, когда я пытался использовать его на сервере. Хороший момент, чтобы упомянуть.
Si8 09
Это будет работать, но медленно. Лучше всего скопировать в буфер обмена и вставить в Excel. Если вы работаете с более чем 1000 записями, это займет некоторое время.
Alex M
25
Решение основано на статье tuncalik (спасибо за идею), но в случае больших таблиц работает намного быстрее (и немного менее понятно).
publicstaticclassMy_DataTable_Extensions{/// <summary>/// Export DataTable to Excel file/// </summary>/// <param name="DataTable">Source DataTable</param>/// <param name="ExcelFilePath">Path to result file name</param>publicstaticvoidExportToExcel(thisSystem.Data.DataTableDataTable,stringExcelFilePath=null){try{intColumnsCount;if(DataTable==null||(ColumnsCount=DataTable.Columns.Count)==0)thrownewException("ExportToExcel: Null or empty input table!\n");// load excel, and create a new workbookMicrosoft.Office.Interop.Excel.ApplicationExcel=newMicrosoft.Office.Interop.Excel.Application();Excel.Workbooks.Add();// single worksheetMicrosoft.Office.Interop.Excel._WorksheetWorksheet=Excel.ActiveSheet;object[]Header=newobject[ColumnsCount];// column headings for(int i =0; i <ColumnsCount; i++)Header[i]=DataTable.Columns[i].ColumnName;Microsoft.Office.Interop.Excel.RangeHeaderRange=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;// DataCellsintRowsCount=DataTable.Rows.Count;object[,]Cells=newobject[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 fielpathif(ExcelFilePath!=null&&ExcelFilePath!=""){try{Worksheet.SaveAs(ExcelFilePath);Excel.Quit();System.Windows.MessageBox.Show("Excel file saved!");}catch(Exception ex){thrownewException("ExportToExcel: Excel file could not be saved! Check filepath.\n"+ ex.Message);}}else// no filepath is given{Excel.Visible=true;}}catch(Exception ex){thrownewException("ExportToExcel: \n"+ ex.Message);}}}
Ответ tuncalik занял у меня почти минуту, это 1 секунда, если это займет много времени ... Я действительно был поражен.
Wilsu
2
Это самый быстрый образец, который я пробовал, отличная работа. Мне пришлось использовать Marshal, чтобы выпустить файл позже. Excel.Quit(); Marshal.FinalReleaseComObject(Worksheet); Marshal.FinalReleaseComObject(HeaderRange); Marshal.FinalReleaseComObject(Excel);
Дэйв Келли
Требуется ли установка Office?
Паршурам Калвикатте
В остальном работает отлично, но мой фоновый цвет фона заголовка всегда установлен на черный при использовании этого решения в консольном приложении. Что может быть причиной??
Завид Аббаси,
15
Попробуйте эту функцию передать данные и путь к файлу, куда вы хотите экспортировать
publicvoidCreateCSVFile(refDataTable dt,string strFilePath){try{// Create the CSV file to which grid data will be exported.StreamWriter sw =newStreamWriter(strFilePath,false);// First we will write the headers.//DataTable dt = m_dsProducts.Tables[0];int iColCount = dt.Columns.Count;for(int i =0; i < iColCount; i++){
sw.Write(dt.Columns[i]);if(i < iColCount -1){
sw.Write(",");}}
sw.Write(sw.NewLine);// Now write all the rows.foreach(DataRow dr in dt.Rows){for(int i =0; i < iColCount; i++){if(!Convert.IsDBNull(dr[i])){
sw.Write(dr[i].ToString());}if(i < iColCount -1){
sw.Write(",");}}
sw.Write(sw.NewLine);}
sw.Close();}catch(Exception ex){throw ex;}}
Обратите внимание, что на самом деле это не будет использовать ячейки таблицы в документе Excel, вместо этого все для каждой строки будет напечатано в первой ячейке каждой строки.
Banshee
@Banshee Нет, Excel полностью понимает файлы CSV.
NickG
С моим excel тоже не работает. Данные каждой строки находятся в первой ячейке.
Mitulát báti
5
Самый лучший и простой способ
privatevoid exportToExcel(DataTable dt){/*Set up work book, work sheets, and excel application*/Microsoft.Office.Interop.Excel.Application oexcel =newMicrosoft.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 =newMicrosoft.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);}}
(пожалуйста, будьте здесь), если существует более одного открытого файла Excel, эта функция выпуска уничтожает их все или только один, переданный в качестве параметра?
Эллиотт Адди
1
Excel Interop:
Этот метод предотвращает перевод даты с дд-мм-гггг на мм-дд-гггг
publicboolDataTableToExcelFile(DataTable dt,string targetFile){constbool dontSave =false;bool success =true;//Exit if there is no rows to exportif(dt.Rows.Count==0)returnfalse;object misValue =System.Reflection.Missing.Value;List<int> dateColIndex =newList<int>();Excel.Application excelApp =newExcel.Application();Excel.Workbook excelWorkBook = excelApp.Workbooks.Add(misValue);Excel.Worksheet excelWorkSheet = excelWorkBook.Sheets("sheet1");//Iterate through the DataTable and populate the Excel work sheettry{for(int i =-1; i <= dt.Rows.Count-1; i++){for(int j =0; j <= dt.Columns.Count-1; j++){if(i <0){//Take special care with Date columnsif(dt.Columns(j).DataTypeistypeof(DateTime)){
excelWorkSheet.Cells(1, j +1).EntireColumn.NumberFormat="d-MMM-yyyy;@";
dateColIndex.Add(j);}//else if ... Feel free to add more Formatselse{//Otherwise Format the column as text
excelWorkSheet.Cells(1, j +1).EntireColumn.NumberFormat="@";}
excelWorkSheet.Cells(1, j +1)= dt.Columns(j).Caption;}elseif(dateColIndex.IndexOf(j)>-1){
excelWorkSheet.Cells(i +2, j +1)=Convert.ToDateTime(dt.Rows(i).ItemArray(j)).ToString("d-MMM-yyyy");}else{
excelWorkSheet.Cells(i +2, j +1)= dt.Rows(i).ItemArray(j).ToString();}}}//Add Autofilters to the Excel work sheet
excelWorkSheet.Cells.AutoFilter(1,Type.Missing,Excel.XlAutoFilterOperator.xlAnd,Type.Missing,true);//Autofit columns for neatness
excelWorkSheet.Columns.AutoFit();if(File.Exists(exportFile))File.Delete(exportFile);
excelWorkSheet.SaveAs(exportFile);}catch{
success =false;}finally{//Do this irrespective of whether there was an exception or not.
excelWorkBook.Close(dontSave);
excelApp.Quit();
releaseObject(excelWorkSheet);
releaseObject(excelWorkBook);
releaseObject(excelApp);}return success;}
Если вас не волнует, что даты переворачиваются, используйте ссылку, которая показывает, как заполнить все ячейки в электронной таблице Excel в одной строке кода:
Просто воспользуйтесь библиотекой CloseMXL.Excel . Это тоже легко и довольно быстро.
Класс
privateDataTable getAllList(){string constr =ConfigurationManager.ConnectionStrings["RConnection"].ConnectionString;
using (SqlConnection con =newSqlConnection(constr)){
using (SqlCommand cmd =newSqlCommand("SELECT EmpId, gender, EmpName, pOnHold FROM Employee WHERE EmpId= '"+AnyVariable+"' ORDER BY EmpName")){
using (SqlDataAdapter da =newSqlDataAdapter()){DataTable dt =newDataTable();
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;}}}}
Затем другой метод, который получает набор данных
publicDataSet getDataSetExportToExcel(){DataSet ds =newDataSet();DataTable dtEmp =newDataTable("CLOT List");
dtEmp = getAllList();
ds.Tables.Add(dtEmp);
ds.Tables[0].TableName="Employee";//If you which to use Mutliple Tabsreturn ds;}
Теперь вы нажимаете кнопку
protectedvoid btn_Export_Click(object sender,EventArgs e){DataSet ds = getDataSetExportToExcel();
using (XLWorkbook wb =newXLWorkbook()){
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 (MemoryStreamMyMemoryStream=newMemoryStream()){
wb.SaveAs(MyMemoryStream);MyMemoryStream.WriteTo(Response.OutputStream);Response.Flush();Response.End();}}}
Вы можете использовать мою библиотеку SwiftExcel . Это особенно хорошо, когда имеет место производительность и низкое использование памяти, поскольку данные записываются непосредственно в файл:
using (var ew =newExcelWriter("C:\\temp\\test.xlsx")){for(var row =1; row <=100; row++){for(var col =1; col <=10; col++){
ew.Write($"row:{row}-col:{col}", col, row);}}}
Я хотел добавить этот ответ, потому что я потратил много времени на поиск быстрого и надежного метода для этого, и нигде не было полных примеров использования OpenXMLWriter для этой цели.
Во-первых, COM / Interop (который используют многие другие ответы) подходит для этой цели, но он страдает некоторыми чувствительностью. Я использовал его десятилетиями, и он в основном стабилен, но при реализации внешнего интерфейса хранилища данных для сотен пользователей я обнаружил, что он подвержен слишком большому количеству проблем в зависимости от машины и того, что делал пользователь, поэтому я переключился на OpenXML. OpenXML DOM довольно хорош для этой цели, но он медленнее, чем использование OpenXMLWriter. Когда вы попадаете в большие наборы данных (100K +) с большим количеством столбцов, DOM намного медленнее, чем OpenXMLWriter, поэтому я использую последний. Приведенный ниже метод записывает 420K + строк с 30+ полями менее чем за 30 секунд.
Я надеюсь, что комментариев достаточно, чтобы помочь любому понять, что он делает. Он упрощен тем, что записывает все значения в файл в виде строк, но вы можете реализовать логику для записи различных типов данных (и использования различных форматов ячеек) в зависимости от содержимого ваших данных. Вы также можете адаптировать это для использования в DataGridView (вместо DataTable), изменив всего несколько вещей (а именно циклы по столбцам / строкам).
Ссылка на DocumentFormat.OpenXML (d / l с OpenXML SDK) и WindowsBase обязательна.
ImportsDocumentFormat.OpenXmlImportsDocumentFormat.OpenXml.SpreadsheetImportsDocumentFormat.OpenXml.PackagingPublicSubExportToExcelXML(ByRef dt AsDataTable, filename AsString)Dim wbp AsWorkbookPart, wsp AsWorksheetPart'IfthisDataTable has more rows in it than can fit inExcel,throw an exception
If dt.Rows.Count>1048575ThenThrowNewException("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 OpenXMLSpreadsheetDocument...Using xls =SpreadsheetDocument.Create(filename,SpreadsheetDocumentType.Workbook)'Add a WorkbookPart to the SpreadsheetDoc, then add a WorksheetPart to the WorkbookPart.
wbp = xls.AddWorkbookPart()
wsp = wbp.AddNewPart(OfWorksheetPart)'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 AsWorkbookStylesPart= wbp.AddNewPart(OfWorkbookStylesPart)Dim ss AsNewStylesheet'Create the only two Fonts we're going to use (Regular and Bold).Dim fBold AsNewFont
fBold.Append(NewBold)Dim fnts AsNewFonts
fnts.Append(NewFont)'This creates the default (unmodified, regular) Font. It's added first, so its index is0.
fnts.Append(fBold)'This creates the Bold font. It's added second, so its index is1.'Create the default Fill/Border settings (these have to be here, even though I don't set any custom fills/borders).Dim flls AsNewFillsDim brdrs AsNewBorders
flls.Append(NewFill)
brdrs.Append(NewBorder)'Now I have to add formats (NumberFormat and CellFormat).First, you create a NumberFormat.Thisis basically the pattern of
' the format (i.e."@"forText).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 163iswhere the built-inExcel formats end.Dim nbrfmts AsNewNumberingFormats
nbrfmts.Append(NewNumberingFormatWith{.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 AsNewCellFormats()
cellfmts.Append(NewCellFormat()With{.FontId=0,.NumberFormatId=164,.FillId=0,.BorderId=0})
cellfmts.Append(NewCellFormat()With{.FontId=1,.NumberFormatId=164,.Alignment=NewAlignment()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 AsOpenXmlWriter=OpenXmlWriter.Create(wsp)'Write the start element for the Worksheet and the Columns...
oxw.WriteStartElement(NewWorksheet)
oxw.WriteStartElement(NewColumns())'Now I'm going to loop through the columns in the DataTable...For c AsInteger=0To dt.Columns.Count-1'Now we'll get the width for the column.Todothis, we loop through all of the rows and measure the width of the text
' using the defaultExcelFont(currently Font:CalibriSize: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 =NewSystem.Drawing.Font("Calibri",11)Dim wid AsDouble=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 asExcel would make it.Dim tmp AsDouble= g.MeasureString(dt.Columns(c).ColumnName,NewSystem.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.ForEach row AsDataRowIn dt.RowsIf tmp > wid Then wid = tmp
tmp = g.MeasureString(row(c).ToString, fnt).WidthNext'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 AsNewList(OfOpenXmlAttribute)From{NewOpenXmlAttribute("min",Nothing, c +1),NewOpenXmlAttribute("max",Nothing, c +1),NewOpenXmlAttribute("width",Nothing,System.Math.Round((wid -12+5)/7D+1,2))}
oxw.WriteStartElement(NewColumn(), oxa)
oxw.WriteEndElement()Next'CLoseout the Columns collection.
oxw.WriteEndElement()'Write the start element for the SheetData...
oxw.WriteStartElement(NewSheetData)'Write the start element for the Header row.
oxw.WriteStartElement(NewRow)'Loop through the Columnsin the dt.ForEach col AsDataColumnIn dt.Columns'Write a cell for this column's Header.AllHeader cells are written with a DataType of String("str").' I ALSO apply the "Header"CellFormat(StyleIndex1) to all of the HeaderCells.This makes them Bold and Centered.WriteCell(oxw, col.ColumnName,"str",1)Next'Closeout the Header row.
oxw.WriteEndElement()'Loop through all of the rows in the dt...ForEach row AsDataRowIn dt.Rows'Write a StartElementforthis row...
oxw.WriteStartElement(NewRow)'Loop through all of the columns in the dt...For c AsInteger=0To dt.Columns.Count-1'Write a valueinthis row/column to the Excel file. I use the datatype of "String" and the defaultCellFormat/StyleIndex.WriteCell(oxw, row(c).ToString,"str",0)Next'Closeoutthis row.
oxw.WriteEndElement()Next'Closeout the Worksheet and SheetData elements...
oxw.WriteEndElement()
oxw.WriteEndElement()EndUsing'Now we're going to create an OpenXMLWriter using the WorkbookPart(that we created above)...Using oxw AsOpenXmlWriter=OpenXmlWriter.Create(wbp)'Add starting elements for the Workbook and Sheets collection.
oxw.WriteStartElement(NewWorkbook())
oxw.WriteStartElement(NewSheets())'Add the Sheet(name the Sheet after the file name minus the extension).
oxw.WriteElement(NewSheet()With{.Name=Path.GetFileNameWithoutExtension(filename),.SheetId=1,.Id= xls.WorkbookPart.GetIdOfPart(wsp)})'WriteEnd elements for the Workbook/Sheets
oxw.WriteEndElement()
oxw.WriteEndElement()EndUsingEndUsingEndSub'ThisSubis used to write a value to a Cell using OpenXMLWriter.PrivateSubWriteCell(ByRef oxw AsOpenXmlWriter,valueAsString, datatype AsString, style AsUInt32Value)Dim oxa AsNewList(OfOpenXmlAttribute)From{NewOpenXmlAttribute("t",Nothing, datatype),NewOpenXmlAttribute("s",Nothing, style)}
oxw.WriteStartElement(NewCell(), oxa)Ifvalue<>NothingThen oxw.WriteElement(NewCellValue(value))
oxw.WriteEndElement()EndSub
Большое спасибо за то, что уделили время этому ответу. У меня есть клиент, у которого есть рабочее решение в Excel Interop, но он жалуется на то, насколько оно медленное. Я видел несколько других ответов на вопросы, которые привели меня к OpenXML, но я рад, что у меня есть ярлык для начала работы.
Брэндон Баркли,
1
Нет проблем. Я все еще использую COM, но только в тех средах, над которыми я полностью контролирую. Я использовал этот подход OpenXML в приложении с парой сотен пользователей в течение пары месяцев, и у меня не было никаких проблем по сравнению с еженедельными ошибками с COM. Я также изучал сторонние решения, подобные упомянутым здесь, но я предпочитаю писать их сам, чтобы сделать их максимально эффективными.
WATYF
0
Что касается ответа tuncalik , это здорово, особенно если вы хотите немного поиграть с кодом :), но он помещает мои даты в Excel в американском формате, т.е. 2 марта 2014 года в Великобритании - 02/03/2014, но в США - 02.03.2014 с первым месяцем, затем днем недели. Мне нужно иметь его в британском формате, пожалуйста, есть идеи?
Я проверил, что он хранится в британском формате в моей таблице данных, а также мой Excel настроен на Великобританию, но по какой-то причине, когда он создает документ Excel, он думает, что это США (это потому, что Microsoft - компания из США :)
Я попробую поэкспериментировать с кодами культуры, но пока не знаю, куда это поместить. Пытался, но это не дало результата.
пс
Мне пришлось изменить одну строку, чтобы заставить ее работать, добавив «приведение», как показано ниже
// single worksheetExcel._Worksheet workSheet =(Excel._Worksheet)excelApp.ActiveSheet;
Обновление: я добился форматирования дат в Великобритании путем преобразования в формат LongDateTime, но это всего лишь работа.
Старый поток - но подумал, что брошу сюда свой код. Я написал небольшую функцию для записи таблицы данных на новый лист Excel по указанному пути (месту). Также вам нужно будет добавить ссылку на библиотеку microsoft excel 14.0.
Я использовал это, чтобы экстраполировать, как писать данные
* обратите внимание, что в операторах catch у меня есть ссылка на статический класс обработчика ошибок (вы можете игнорировать их)
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 privatevoidWriteToExcel(System.Data.DataTable dt,string location){//instantiate excel objects (application, workbook, worksheets)
excel.ApplicationXlObj=new excel.Application();XlObj.Visible=false;
excel._WorkbookWbObj=(excel.Workbook)(XlObj.Workbooks.Add(""));
excel._WorksheetWsObj=(excel.Worksheet)WbObj.ActiveSheet;//run through datatable and assign cells to values of datatabletry{int row =1;int col =1;foreach(DataColumn column in dt.Columns){//adding columnsWsObj.Cells[row, col]= column.ColumnName;
col++;}//reset column and row variables
col =1;
row++;for(int i =0; i < dt.Rows.Count; i++){//adding dataforeach(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();}}
Это работает хорошо, однако впоследствии вы никогда не убьете свои процессы Excel, поэтому я предлагаю добавить это, заменив вашу SaveAsстроку, как она здесь: 'XlObj.DisplayAlerts = false; WbObj.SaveAs (местоположение); WbObj.Close (); XlObj.Quit (); Marshal.ReleaseComObject (WsObj); Marshal.ReleaseComObject (WbObj); Marshal.ReleaseComObject (XlObj); ' Чтобы использовать Marshal.ReleaseComObjectметод, добавьте using System.Runtime.InteropServicesсборку в свой проект.
GrammatonCleric
0
Один из способов сделать это - также с помощью ACE OLEDB Provider (см. Также строки подключения для Excel ). Конечно, вам нужно будет установить и зарегистрировать провайдера. Он должен быть у вас, если у вас установлен Excel, но это необходимо учитывать при развертывании приложения.
Это пример вызова вспомогательного метода из ExportHelper:ExportHelper.CreateXlsFromDataTable(myDataTable, @"C:\tmp\export.xls");
Помощник для экспорта в файл Excel с помощью ACE OLEDB:
publicclassExportHelper{privateconststringExcelOleDbConnectionStringTemplate="Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 8.0;HDR=YES\";";/// <summary>/// Creates the Excel file from items in DataTable and writes them to specified output file./// </summary>publicstaticvoidCreateXlsFromDataTable(DataTable dataTable,string fullFilePath){string createTableWithHeaderScript =GenerateCreateTableCommand(dataTable);
using (var conn =newOleDbConnection(String.Format(ExcelOleDbConnectionStringTemplate, fullFilePath))){if(conn.State!=ConnectionState.Open){
conn.Open();}OleDbCommand cmd =newOleDbCommand(createTableWithHeaderScript, conn);
cmd.ExecuteNonQuery();foreach(DataRow dataExportRow in dataTable.Rows){AddNewRow(conn, dataExportRow);}}}privatestaticvoidAddNewRow(OleDbConnection conn,DataRow dataRow){string insertCmd =GenerateInsertRowCommand(dataRow);
using (OleDbCommand cmd =newOleDbCommand(insertCmd, conn)){AddParametersWithValue(cmd, dataRow);
cmd.ExecuteNonQuery();}}/// <summary>/// Generates the insert row command./// </summary>privatestaticstringGenerateInsertRowCommand(DataRow dataRow){var stringBuilder =newStringBuilder();var columns = dataRow.Table.Columns.Cast<DataColumn>().ToList();var columnNamesCommaSeparated =string.Join(",", columns.Select(x => x.Caption));var questionmarkCommaSeparated =string.Join(",", columns.Select(x =>"?"));
stringBuilder.AppendFormat("INSERT INTO [{0}] (", dataRow.Table.TableName);
stringBuilder.Append(columnNamesCommaSeparated);
stringBuilder.Append(") VALUES(");
stringBuilder.Append(questionmarkCommaSeparated);
stringBuilder.Append(")");return stringBuilder.ToString();}/// <summary>/// Adds the parameters with value./// </summary>privatestaticvoidAddParametersWithValue(OleDbCommand cmd,DataRow dataRow){var paramNumber =1;for(int i =0; i <= dataRow.Table.Columns.Count-1; i++){if(!ReferenceEquals(dataRow.Table.Columns[i].DataType,typeof(int))&&!ReferenceEquals(dataRow.Table.Columns[i].DataType,typeof(decimal))){
cmd.Parameters.AddWithValue("@p"+ paramNumber, dataRow[i].ToString().Replace("'","''"));}else{objectvalue=GetParameterValue(dataRow[i]);OleDbParameter parameter = cmd.Parameters.AddWithValue("@p"+ paramNumber,value);if(valueisdecimal){
parameter.OleDbType=OleDbType.Currency;}}
paramNumber = paramNumber +1;}}/// <summary>/// Gets the formatted value for the OleDbParameter./// </summary>privatestaticobjectGetParameterValue(objectvalue){if(valueisstring){returnvalue.ToString().Replace("'","''");}returnvalue;}privatestaticstringGenerateCreateTableCommand(DataTable tableDefination){StringBuilder stringBuilder =newStringBuilder();bool firstcol =true;
stringBuilder.AppendFormat("CREATE TABLE [{0}] (", tableDefination.TableName);foreach(DataColumn tableColumn in tableDefination.Columns){if(!firstcol){
stringBuilder.Append(", ");}
firstcol =false;string columnDataType ="CHAR(255)";switch(tableColumn.DataType.Name){case"String":
columnDataType ="CHAR(255)";break;case"Int32":
columnDataType ="INTEGER";break;case"Decimal":// Use currency instead of decimal because of bug described at // http://social.msdn.microsoft.com/Forums/vstudio/en-US/5d6248a5-ef00-4f46-be9d-853207656bcc/localization-trouble-with-oledbparameter-and-decimal?forum=csharpgeneral
columnDataType ="CURRENCY";break;}
stringBuilder.AppendFormat("{0} {1}", tableColumn.ColumnName, columnDataType);}
stringBuilder.Append(")");return stringBuilder.ToString();}}
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
{publicclassExcelProcesser{publicvoidWriteToExcel(System.Data.DataTable dt){
excel.ApplicationXlObj=new excel.Application();XlObj.Visible=false;
excel._WorkbookWbObj=(excel.Workbook)(XlObj.Workbooks.Add(""));
excel._WorksheetWsObj=(excel.Worksheet)WbObj.ActiveSheet;object misValue =System.Reflection.Missing.Value;try{int row =1;int col =1;foreach(DataColumn column in dt.Columns){//adding columnsWsObj.Cells[row, col]= column.ColumnName;
col++;}//reset column and row variables
col =1;
row++;for(int i =0; i < dt.Rows.Count; i++){//adding dataforeach(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);}}}
Это решение в основном отправляет List<Object>данные в Excel. Для этого используется DataTable. Я реализовал метод расширения, поэтому в основном необходимы две вещи. 1. Метод расширения.
publicstaticclassReportHelper{publicstaticstringToExcel<T>(thisIList<T> data){PropertyDescriptorCollection properties =TypeDescriptor.GetProperties(typeof(T));DataTable table =newDataTable();foreach(PropertyDescriptor prop in properties){//table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);if(prop.Attributes[typeof(FGMS.Entity.Extensions.ReportHeaderAttribute)]!=null){
table.Columns.Add(GetColumnHeader(prop),Nullable.GetUnderlyingType(prop.PropertyType)?? prop.PropertyType);}}//So it seems like when there is only one row of data the headers do not appear//so adding a dummy blank row which fixed the issues//Add a blank Row - Issue # 1471DataRow blankRow = table.NewRow();
table.Rows.Add(blankRow);foreach(T item in data){DataRow row = table.NewRow();foreach(PropertyDescriptor prop in properties)//row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;if(prop.Attributes[typeof(FGMS.Entity.Extensions.ReportHeaderAttribute)]!=null){
row[GetColumnHeader(prop)]= prop.GetValue(item)??DBNull.Value;}
table.Rows.Add(row);}
table.TableName="Results";var filePath =System.IO.Path.GetTempPath()+"\\"+System.Guid.NewGuid().ToString()+".xls";
table.WriteXml(filePath);return filePath;}privatestaticstringGetColumnHeader(PropertyDescriptor prop){return((FGMS.Entity.Extensions.ReportHeaderAttribute)(prop.Attributes[typeof(FGMS.Entity.Extensions.ReportHeaderAttribute)])).ReportHeaderText;}}
Украсьте свои классы DTO Атрибутом [ReportHeaderAttribute("Column Name")]
Все, что должно быть столбцом в Excel, должно быть украшено [ReportHeaderAttribute("Column Name")]
Тогда просто
Var userList =Service.GetUsers()//Returns List of UserDTO;var excelFilePath = userList.ToExcel();HttpResponseMessage result =newHttpResponseMessage(HttpStatusCode.OK);var stream =newFileStream(excelFilePath,FileMode.Open);
result.Content=newStreamContent(stream);
result.Content.Headers.ContentType=newMediaTypeHeaderValue("application/vnd.ms-excel");
result.Content.Headers.ContentDisposition=newContentDispositionHeaderValue("attachment"){FileName="UserList.xls"};return result;
и если оператор не хочет создавать DTO для каждой таблицы, с которой они собираются запускать это? Как, например, это делает foreach из моей 1 тысячи таблиц. Добавление атрибута заголовка отчета происходит не на лету - там много кода и это еще до того, как приступить к реальной работе. Не ударить по вашему решению - просто указать, что принцип лени здесь не используется, поскольку это решение добавляет шаг создания dto и последующей компиляции ... Я должен заявить - мне нравится, что вы использовали дженерики.
Кен
0
Для экспорта данных в Excel вы можете использовать библиотеку ClosedXML.Report ( https://github.com/ClosedXML/ClosedXML.Report ). Поверьте, это замечательная библиотека, которой ей легко пользоваться. Библиотеке не требуется взаимодействие с Excel. ClosedXML.Report создает файл Excel на основе шаблона, который можно создать в Excel с использованием любого форматирования. Например:
var template =newXLTemplate(@".\Templates\report.xlsx");
using (var db =newDbDemos()){var cust = db.customers.LoadWith(c => c.Orders).First();
template.AddVariable(cust);
template.Generate();}
template.SaveAs(outputFile);
Private tmr AsSystem.Windows.Forms.TimerPrivateSubTestExcel()HandlesButton1.Click'// Initial data: SQL Server table with 6 columns and 293000 rows.'// Data table holding all dataDim dt AsNewDataTable("F161")'// Create connectionDim conn AsNewSqlConnection("Server=MYSERVER;Database=Test;Trusted_Connection=Yes;")Dim fAdapter AsNewSqlDataAdapterWith{.SelectCommand=NewSqlCommand($"SELECT * FROM dbo.MyTable", conn)}'// Fill DataTable
fAdapter.Fill(dt)'// Create Excel applicationDim xlApp AsNewExcel.ApplicationWith{.Visible=True}'// Temporarily disable screen updating
xlApp.ScreenUpdating=False'// Create brand new workbookDim xlBook AsExcel.Workbook= xlApp.Workbooks.Add()Dim xlSheet AsExcel.Worksheet=DirectCast(xlBook.Sheets(1),Excel.Worksheet)'// Get number of rowsDim rows_count = dt.Rows.Count'// Get number of columnsDim 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 Rangeobject 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 arrayFor r =0To rows_count -1For c =0To cols_count -1
arr(r +1, c +1)= dt.Rows(r)(c)NextNext'// ========================================================='// 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 =NewTimer()AddHandler tmr.Tick,Sub()
GC.Collect()
GC.WaitForPendingFinalizers()
GC.WaitForFullGCComplete()
tmr.Dispose()EndSub
tmr.Interval=TimeSpan.FromSeconds(2).TotalMilliseconds()
tmr.Start()EndSub
publicclassTestObject{publicintCol1{get;set;}publicintCol2{get;set;}publicstringCol3{get;set;}publicDateTimeCol4{get;set;}}[TestMethod]publicvoidLoadFromCollection_MemberList_Test(){///programming/32587834/epplus-loadfromcollection-text-converted-to-number/32590626#32590626varTestObjectList=newList<TestObject>();for(var i =0; i <10; i++)TestObjectList.Add(newTestObject{Col1= i,Col2= i*10,Col3=(i*10)+"E4"});//Create a test filevar fi =newFileInfo(@"c:\temp\LoadFromCollection_MemberList_Test.xlsx");if(fi.Exists)
fi.Delete();
using (var pck =newExcelPackage(fi)){//Do NOT include Col1var mi =typeof(TestObject).GetProperties().Where(pi => pi.Name!="Col1").Select(pi =>(MemberInfo)pi).ToArray();var worksheet = pck.Workbook.Worksheets.Add("Sheet1");
worksheet.Cells.LoadFromCollection(TestObjectList,true,TableStyles.Dark1,BindingFlags.Public|BindingFlags.Instance, mi);
pck.Save();}}
Ответы:
Я бы порекомендовал ClosedXML -
Вы можете превратить DataTable в лист Excel с помощью очень удобочитаемого кода:
Разработчик отзывчивый и отзывчивый. Проект активно развивается, документация отличная.
источник
ClosedXML.Excel.XLWorkbook
Попробуйте простой код, чтобы преобразовать DataTable в файл Excel как csv:
Это запишет новый файл
excel.csv
в текущий рабочий каталог, который обычно либо находится там, где находится .exe, либо откуда вы его запускаете.источник
"excel.csv"
место?,
(запятую), то по стандарту CSV эта ячейка должна быть заключена в кавычки,","
а затем отображаться в файле как обычно. Итак, да - это вызовет проблему, потому что этот код не обнаруживает,
и не применяет кавычки.Элегантный вариант - написать метод расширения (см. Ниже) для класса DataTable инфраструктуры .NET.
Этот метод расширения можно назвать следующим образом:
Метод расширения для класса DataTable:
источник
ExcelFilePath != null && ExcelFilePath != ""
может быть!String.IsNullOrEmpty(ExcelFilePath)
Решение основано на статье tuncalik (спасибо за идею), но в случае больших таблиц работает намного быстрее (и немного менее понятно).
источник
Excel.Quit(); Marshal.FinalReleaseComObject(Worksheet); Marshal.FinalReleaseComObject(HeaderRange); Marshal.FinalReleaseComObject(Excel);
Попробуйте эту функцию передать данные и путь к файлу, куда вы хотите экспортировать
источник
Самый лучший и простой способ
источник
private void releaseObject(object o) { try { while (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0) { } } catch { } finally { o = null; } }
Excel Interop:
Excel Interop - эффективность и производительность
CSV:
источник
Класс
Затем другой метод, который получает набор данных
Теперь вы нажимаете кнопку
источник
Вы можете использовать мою библиотеку SwiftExcel . Это особенно хорошо, когда имеет место производительность и низкое использование памяти, поскольку данные записываются непосредственно в файл:
Команда Nuget для установки:
источник
Я хотел добавить этот ответ, потому что я потратил много времени на поиск быстрого и надежного метода для этого, и нигде не было полных примеров использования OpenXMLWriter для этой цели.
Во-первых, COM / Interop (который используют многие другие ответы) подходит для этой цели, но он страдает некоторыми чувствительностью. Я использовал его десятилетиями, и он в основном стабилен, но при реализации внешнего интерфейса хранилища данных для сотен пользователей я обнаружил, что он подвержен слишком большому количеству проблем в зависимости от машины и того, что делал пользователь, поэтому я переключился на OpenXML. OpenXML DOM довольно хорош для этой цели, но он медленнее, чем использование OpenXMLWriter. Когда вы попадаете в большие наборы данных (100K +) с большим количеством столбцов, DOM намного медленнее, чем OpenXMLWriter, поэтому я использую последний. Приведенный ниже метод записывает 420K + строк с 30+ полями менее чем за 30 секунд.
Я надеюсь, что комментариев достаточно, чтобы помочь любому понять, что он делает. Он упрощен тем, что записывает все значения в файл в виде строк, но вы можете реализовать логику для записи различных типов данных (и использования различных форматов ячеек) в зависимости от содержимого ваших данных. Вы также можете адаптировать это для использования в DataGridView (вместо DataTable), изменив всего несколько вещей (а именно циклы по столбцам / строкам).
Ссылка на DocumentFormat.OpenXML (d / l с OpenXML SDK) и WindowsBase обязательна.
источник
Что касается ответа tuncalik , это здорово, особенно если вы хотите немного поиграть с кодом :), но он помещает мои даты в Excel в американском формате, т.е. 2 марта 2014 года в Великобритании - 02/03/2014, но в США - 02.03.2014 с первым месяцем, затем днем недели. Мне нужно иметь его в британском формате, пожалуйста, есть идеи?
Я проверил, что он хранится в британском формате в моей таблице данных, а также мой Excel настроен на Великобританию, но по какой-то причине, когда он создает документ Excel, он думает, что это США (это потому, что Microsoft - компания из США :)
Я попробую поэкспериментировать с кодами культуры, но пока не знаю, куда это поместить. Пытался, но это не дало результата.
пс
Мне пришлось изменить одну строку, чтобы заставить ее работать, добавив «приведение», как показано ниже
Обновление: я добился форматирования дат в Великобритании путем преобразования в формат LongDateTime, но это всего лишь работа.
ура.
источник
Вы можете использовать EasyXLS - библиотеку для экспорта файлов Excel.
Проверьте этот код:
См. Также этот пример о том, как экспортировать datatable в Excel на C # .
источник
Старый поток - но подумал, что брошу сюда свой код. Я написал небольшую функцию для записи таблицы данных на новый лист Excel по указанному пути (месту). Также вам нужно будет добавить ссылку на библиотеку microsoft excel 14.0.
Я вытащил из этой темы, чтобы написать что-нибудь в Excel - Как записать данные в файл Excel (.xlsx)
Я использовал это, чтобы экстраполировать, как писать данные
* обратите внимание, что в операторах catch у меня есть ссылка на статический класс обработчика ошибок (вы можете игнорировать их)
источник
SaveAs
строку, как она здесь: 'XlObj.DisplayAlerts = false; WbObj.SaveAs (местоположение); WbObj.Close (); XlObj.Quit (); Marshal.ReleaseComObject (WsObj); Marshal.ReleaseComObject (WbObj); Marshal.ReleaseComObject (XlObj); ' Чтобы использоватьMarshal.ReleaseComObject
метод, добавьтеusing System.Runtime.InteropServices
сборку в свой проект.Один из способов сделать это - также с помощью ACE OLEDB Provider (см. Также строки подключения для Excel ). Конечно, вам нужно будет установить и зарегистрировать провайдера. Он должен быть у вас, если у вас установлен Excel, но это необходимо учитывать при развертывании приложения.
Это пример вызова вспомогательного метода из
ExportHelper
:ExportHelper.CreateXlsFromDataTable(myDataTable, @"C:\tmp\export.xls");
Помощник для экспорта в файл Excel с помощью ACE OLEDB:
источник
используйте следующий класс
}
источник
Все, что должно быть столбцом в Excel, должно быть украшено
[ReportHeaderAttribute("Column Name")]
Тогда просто
источник
Для экспорта данных в Excel вы можете использовать библиотеку ClosedXML.Report ( https://github.com/ClosedXML/ClosedXML.Report ). Поверьте, это замечательная библиотека, которой ей легко пользоваться. Библиотеке не требуется взаимодействие с Excel. ClosedXML.Report создает файл Excel на основе шаблона, который можно создать в Excel с использованием любого форматирования. Например:
источник
источник
Чисто образец кода (на случай, если он может помочь кому-то с некоторыми идеями), основанный на ответе Томаша Висьневского отсюда: https://stackoverflow.com/a/21079709/2717521
MainWindow ExportButton:
ExportToExcel Класс:
источник
С пакетом NuGet EPPlus это очень просто .
Обратите внимание, что
Col1
НЕТ в выводе:источник