I am exporting a 1200 X 800 matrix (indexMatrix) to a excel file using the standard Microsoft.Office.Interop.Excel. The app works, just that it is really really really slow( even for the 100 x 100 matrix) . I also export in a text file through a TextWriter an it works almost instantly . Is there any way to export to the excel file faster?
Here is my code :
Excel.Application xlApp=new Excel.Application();
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
object misValue = System.Reflection.Missing.Value;
//xlApp = new Excel.ApplicationClass();
xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
for (int i = 0; i < 800; i++) //h
for (int j = 0; j < 1200; j++)
xlWorkSheet.Cells[i+1,j+1] =indexMatrix[i][j];
xlWorkBook.SaveAs("C:\\a.xls", Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
xlWorkBook.Close(true, misValue, misValue);
xlApp.Quit();
releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlApp);
MessageBox.Show("Excel file created , y开发者_运维知识库ou can find the file c:\\csharp-Excel.xls");
You are updating individual cells. That's going to be very slow. If you think about it, each time you update a cell, an RPC call will be marshalled to the Excel process.
It will be much faster if you assign your two dimensional array of values to an Excel Range of the same dimensions in a single statement (one cross-process call) instead of your current 1200 x 800 = 960,000 cross-process calls.
Something like:
// Get dimensions of the 2-d array
int rowCount = indexMatrix.GetLength(0);
int columnCount = indexMatrix.GetLength(1);
// Get an Excel Range of the same dimensions
Excel.Range range = (Excel.Range) xlWorkSheet.Cells[1,1];
range = range.get_Resize(rowCount, columnCount);
// Assign the 2-d array to the Excel Range
range.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, indexMatrix);
Actually, to be pedantic, there are three cross-process calls in the above code (.Cells, .get_Resize and .set_Value), and there are two calls per iteration in your code (.Cells get and an implicit .set_Value) for a total of 1200 x 800 x 2 = 1,920,000.
Note
range.get_Resize
and range.set_Value
were needed for an old version of the Excel interop library I was using when this post was first authored. These days you can use range.Resize
and range.Value
as noted in the comment by @The1nk.
Excel interop is never going to be fast. You're basically remote-controlling an instance of the Excel application. You might have more success by creating a CSV file and then using Excel interop to convert this to a .xls or .xlsx file
I had similar problems when reading an extremely large excel file and it took over 2 hours using interop.
I tried using ClosedXml and the process took less than 10 seconds. ClosedXml
// To loop
Sheet.Row(y).Cell(x).Value
Also keep in mind interop will not work on your server unless you have excel installed. ClosedXml does not need excel installed.
Turn off ScreenUpdating
before writing any data, Application.ScreenUpdating = FALSE
then turn on at end of code = TRUE
Use Value2 to make it fast; Show excel before filling data
ClosedXML is a miracle, it's a great deal faster and easier to use.
var workbook = new XLWorkbook();//create the new book
var worksheet = workbook.Worksheets.Add("Computer Install");// Add a sheet
worksheet.Cell(1,1).Value = "PC Name";// (Row, column) write to a cell
workbook.SaveAs(@"LIC documents.xlsx");// Save the book
You install using a nu Get package. https://www.nuget.org/packages/ClosedXML
I am answering a little bit late sorry. I was working on my project and we had to use interop excel. And our data was too big, which was taking more than 1 minute with interop excel. We tried something else which is copy the all content of datagridview to clipboard, open an excel worksheet using interop excel, and paste the content to excel. It takes less than 1 second and exports our data perfectly.
DataGridView to string:
var newline = System.Environment.NewLine;
var tab = "\t";
var clipboard_string = "";
foreach (DataGridViewRow row in dgProjeler.Rows)
{
for (int i = 0; i < row.Cells.Count; i++)
{
if (i == (row.Cells.Count - 1))
clipboard_string += row.Cells[i].Value + newline;
else
clipboard_string += row.Cells[i].Value + tab;
}
}
and copy the string to clipboard
Clipboard.SetText(clipboard_string);
And open a worksheet, paste the content.
Excel.Application app = new Excel.Application();
app.Visible = true;
Excel.Workbook wb = app.Workbooks.Add(1);
Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
// changing the name of active sheet
ws.Name = "Exported from gridview";
ws.Rows.HorizontalAlignment = HorizontalAlignment.Center;
app.ActiveWindow.Activate();
ws.Activate();
ws.Paste();
ws.Cells.EntireColumn.AutoFit();
It works perfectly on me, I hope it helps people who still couldn't find the solution.
There are three ways to do this, 2 of which are mentioned in different answers by others:
- Directly set the value of a range in excel to the 2D array.
- Write data to a CSV file, then use interop to save the CSV file as an xls or xlsx file.
- Write data to a CSV file, then use the data connections feature to use the CSV as a data source and import the data.
All the three methods above are very fast. I could write data with a size of 90000 rows and 100 columns in around 6 seconds.
P.S. They didn't however solve my problem with formatting the data for borders, font styles, colors, cell merging etc.
精彩评论