I am using this code to write data to excel file.
Microsoft.Office.Interop.Excel.Application excelapp = new Microsoft.Office.Interop.Excel.Application();
excelapp.Visible = true;
I am using this code to write to excel.
_Workbook workbook = (_Workbook)(excelapp.Workbooks.Open(#"C:\Path\To\Your\WorkBook\ExcelWorkBook.Xls"));
_Worksheet worksheet = (_Worksheet)workbook.ActiveSheet;
worksheet.Cells[1, 1] = "Name";
worksheet.Cells[1, 2] = "Bid";
worksheet.Cells[2, 1] = txbName.Text;
worksheet.Cells[2, 2] = txbResult.Text;
excelapp.Visible = false;
This code works fine except that it works very slowly. I want a fastest way to write tot excel. If I run a loop to write to excel to write thousands of rows, then it works to slowly. Is there any faster way to write the data to excel file like write a whole datatable to excel file of write datatable rows simultaneously rather than cell by cell ?
Try inserting an array to the excel instead:
object[,] arr = new object[rowCount, columnCount];
//Assign your values here
Excel.Range c1 = (Excel.Range)worksheet.Cells[0, 1];
Excel.Range c2 = (Excel.Range)worksheet.Cells[rowCount - 1, columnCount];
Excel.Range range = wsh.get_Range(c1, c2);
range.Value = arr;
Writing / Reading values to / from each cell is far too slow and will consume a lot of time, try doing it in memory.
Related
I have a C# console application which needs a large Excel to be split into multiple Excel files based on the row count. The code below shows a source file with only 51 rows (including the header column rows) but the final source file will have 100,000+ rows.
The code is trying to skip the very first (header) row and then should copy from rows 2 through 11 and so on--I have the target files set to only 10 rows per file, to make developing faster.
Question So how do I copy rows 2 through 11 and subsequent 10 rows from the source Excel file and paste to multiple target Excel files so that the target files each will have 10 rows?
Here is the almost newly written code. It is loosely based on copying of specific range of excel cells from one worksheet to another worksheet and https://social.msdn.microsoft.com/Forums/vstudio/en-US/afd01976-63d0-4f96-9ba4-e3e2b6cf8d55/excel-with-c-how-to-specify-a-range-?forum=vsto
Now I am able to write 5 Excel files. But the first file has 9 rows (starting from row 2) while 2nd file has only 3 rows, starting with row 10, the 3rd has 13 rows starting, again, with row 10; the last two files have incrementally more rows, both starting with row 10.
So something wrong with my For Loop? Or the way I am selecting the ranges?
string startPath = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
string filePath_source = Path.Combine(startPath, #"Source_Files\Offers_Source_Temp.xlsx");
string filePath_copiedinto = Path.Combine(startPath, #"Source_Files\ToBeCopiedInto.xlsx");
app = new Excel.Application();
app.DisplayAlerts = false;
book = app.Workbooks.Open(filePath_source);
sheet = (Excel.Worksheet)book.Worksheets.get_Item((1));
int iRowCount = sheet.UsedRange.Rows.Count;
int maxrows = 10;//change this to something like 50,000 later. 01/16/18
int maxloops = iRowCount / maxrows;
int beginrow = 2; //skipping the header row.
Excel.Application destxlApp;
Excel.Workbook destworkBook;
Excel.Worksheet destworkSheet;
Excel.Range destrange;
string srcPath;
string destPath;
//Opening of first worksheet and copying
srcPath = filePath_source;
for (int i = 1; i <= maxloops; i++) {
Excel.Range rng = (Excel.Range)sheet.Range[sheet.Cells[beginrow, 1], sheet.Cells[maxrows, 3]];
rng.Copy(Type.Missing);
//opening of the second worksheet and pasting
destPath = filePath_copiedinto;
destxlApp = new Excel.Application();
destxlApp.DisplayAlerts = false;
destworkBook = destxlApp.Workbooks.Open(destPath, 0, false);
destworkSheet = destworkBook.Worksheets.get_Item(1);
destrange = destworkSheet.Cells[1, 1];
destrange.Select();
destworkSheet.Paste(Type.Missing, Type.Missing);
destworkBook.SaveAs(startPath + "\\Output_Files\\" + beginrow + ".xlsx");
destworkBook.Close(true, null, null);
destxlApp.Quit();
beginrow = beginrow + maxrows;
string blah = null;
}
I would suggest to use OpenXml library to do that task. It is dependency free and supports the whole OpenXml structure.
Here a starting point how to read/write the rows:
using System;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
// Open the document for editing.
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
foreach (Row r in sheetData.Elements<Row>())
{
}
}
Now, writing is very similar:
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Create(fileName),
SpreadsheetDocumentType.Workbook))
{
// create the workbook
spreadSheet.AddWorkbookPart();
spreadSheet.WorkbookPart.Workbook = new Workbook (); // create the worksheet
spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
spreadSheet.WorkbookPart.WorksheetParts.First().Worksheet = new Worksheet();
// create sheet data
spreadSheet.WorkbookPart.WorksheetParts.First().Worksheet.AppendChild(new SheetData());
// create row
spreadSheet.WorkbookPart.WorksheetParts.First().Worksheet.First().AppendChild(new Row());
}
Got it! In my revised code in the Question, I came close but had some problem in the For Loop; fixed it per the code below. So here is the almost complete code. Thanks everyone for your help!!
try
{
string startPath = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
string filePath_source = Path.Combine(startPath, #"Source_Files\Offers_Source_Temp.xlsx");
string filePath_copiedinto = Path.Combine(startPath, #"Source_Files\ToBeCopiedInto.xlsx");
app = new Excel.Application();
app.DisplayAlerts = false;
book = app.Workbooks.Open(filePath_source);
sheet = (Excel.Worksheet)book.Worksheets.get_Item((1));
int iRowCount = sheet.UsedRange.Rows.Count;
int countColumns = sheet.UsedRange.Columns.Count;
int maxrows = 10;//change this to something like 50,000 later. 01/16/18
int maxloops = iRowCount / maxrows;
int beginrow = 2; //skipping the header row.
Excel.Application destxlApp;
Excel.Workbook destworkBook;
Excel.Worksheet destworkSheet;
Excel.Range destrange;
string srcPath;
string destPath;
//Opening of first worksheet and copying
srcPath = filePath_source;
for (int i = 1; i <= maxloops; i++) {
/// Excel.Range rng = (Excel.Range)sheet.Range[sheet.Cells[beginrow, 1], sheet.Cells[maxrows, 3]];
Excel.Range startCell = sheet.Cells[beginrow, 1];//not sure the second parameter needed?
Excel.Range endCell = sheet.Cells[beginrow+maxrows-1, 3];//not sure the second parameter needed?
Excel.Range rng = sheet.Range[startCell, endCell];
rng = rng.EntireRow;//so second parameters above should not be needed. But doesn't work without it!
rng.Copy(Type.Missing);
//opening of the second worksheet and pasting
destPath = filePath_copiedinto;
destxlApp = new Excel.Application();
destxlApp.DisplayAlerts = false;
destworkBook = destxlApp.Workbooks.Open(destPath, 0, false);
destworkSheet = destworkBook.Worksheets.get_Item(1);
destrange = destworkSheet.Cells[1, 1];
destrange.Select();
destworkSheet.Paste(Type.Missing, Type.Missing);
destworkBook.SaveAs(startPath + "\\Output_Files\\" + beginrow + ".xlsx");
destworkBook.Close(true, null, null);
destxlApp.Quit();
beginrow = beginrow + maxrows;
}//for loop
}
I am trying to overwrite data in excel sheet. The way I do it is by deleting the content from the the sheet and then writing range of data. The problem is when I write the data it deletes the cell format and then places my numbers as text.
is there a way to keep the format which the user has defined so when I write data it uses the same format?
You can Use the Microsoft.Office.Interop.Excel dll file. Add it in your project reference and in your Code as Using using Excel = Microsoft.Office.Interop.Excel;
Then check the below code. Also to you this dll there should be Microsoft Excel Installed in the machine where this code is going to run.
Excel.Application excel = new Excel.Application();
excel.DisplayAlerts = false;
excel.Workbooks.Add();
Excel.Worksheet worksheet = excel.ActiveSheet;
int rowIndex = 2 ;
worksheet.Cells[1, "A"] = "List Title";
worksheet.Cells[1, "B"] = "Item Count";
Console.WriteLine("Copying the contents to Excel");
foreach (var list in listCollection)
{
worksheet.Cells[rowIndex, "A"] = list.Title;
worksheet.Cells[rowIndex, "B"] = list.ItemCount;
rowIndex++;
//Console.Write("List Title: {0}", list.Title);
//Console.WriteLine("\t"+"Item Count:"+list.ItemCount);
}
string fileName = string.Format(#"C:\FileName.xlsx");
worksheet.SaveAs(fileName);
Console.WriteLine("Export Completed");
Console.ReadLine();
excel.Quit();
GC.Collect();
I am writing a C# program which copies a range of cells from a worksheet of one workbook to a worksheet of an other workbook. But the problem I am facing is I am only able to copy and paste the whole worksheet of first workbook. I want to know how to select only a specific range(from row 5 [column 1 to column 10] to row 100 [column 1 to column 10]) and paste it in second workbook worksheet starting from row 2 column 8.
Also i want to know how a fill a column say from C1 to C100 with some value in a direct way instead of using the loop like below
for(i=1;i<2;i++)
{
for(j=1;j<101;i++)
{
worksheet.cells[i,j]="Fixed";
}
}
Here is the code that i have written so far
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Excel.Application srcxlApp;
Excel.Workbook srcworkBook;
Excel.Worksheet srcworkSheet;
Excel.Range srcrange;
Excel.Application destxlApp;
Excel.Workbook destworkBook;
Excel.Worksheet destworkSheet;
Excel.Range destrange;
string srcPath;
string destPath;
//Opening of first worksheet and copying
srcPath="C:\\Documents and Settings\\HARRY\\Desktop\\incident.csv";
srcxlApp = new Excel.Application();
srcworkBook = srcxlApp.Workbooks.Open(srcPath);
srcworkSheet = srcworkBook.Worksheets.get_Item(1);
srcrange = srcworkSheet.UsedRange;
srcrange.Copy(Type.Missing);
//opening of the second worksheet and pasting
destPath = "C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report.xls";
destxlApp = new Excel.Application();
destworkBook = destxlApp.Workbooks.Open(destPath,0,false);
destworkSheet = destworkBook.Worksheets.get_Item(1);
destrange = destworkSheet.Cells[1, 1];
destrange.Select();
destworkSheet.Paste(Type.Missing, Type.Missing);
destworkBook.SaveAs("C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report " + DateTime.Now.ToString("MM_dd_yyyy") + ".xls");
srcxlApp.Application.DisplayAlerts = false;
destxlApp.Application.DisplayAlerts = false;
destworkBook.Close(true, null, null);
destxlApp.Quit();
srcworkBook.Close(false, null, null);
srcxlApp.Quit();
}
}
}
You should be able to do this:
Excel.Range from = srcworkSheet.Range("C1:C100");
Excel.Range to = destworkSheet.Range("C1:C100");
from.Copy(to);
mrtig has a very elegant solution. But it won't work if you have the workbooks in separate instances of excel. So, the key is to open them in just one instance. I've modified your example to show using this approach:
public void CopyRanges()
{
// only one instance of excel
Excel.Application excelApplication = new Excel.Application();
srcPath="C:\\Documents and Settings\\HARRY\\Desktop\\incident.csv";
Excel.Workbook srcworkBook = excelApplication.Workbooks.Open(srcPath);
Excel.Worksheet srcworkSheet = srcworkBook.Worksheets.get_Item(1);
destPath = "C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report.xls";
Excel.Workbook destworkBook = excelApplication.Workbooks.Open(destPath,0,false);
Excel.Worksheet destworkSheet = destworkBook.Worksheets.get_Item(1);
Excel.Range from = srcworkSheet.Range("C1:C100");
Excel.Range to = destworkSheet.Range("C1:C100");
// if you use 2 instances of excel, this will not work
from.Copy(to);
destworkBook.SaveAs("C:\\Documents and Settings\\HARRY\\Desktop\\FIXED Aging incident Report " + DateTime.Now.ToString("MM_dd_yyyy") + ".xls");
srcxlApp.Application.DisplayAlerts = false;
destxlApp.Application.DisplayAlerts = false;
destworkBook.Close(true, null, null);
srcworkBook.Close(false, null, null);
excelApplication.Quit();
}
For the First part of setting the same value for the entire range, instead of looping following will work out
range1 = workSheet.get_Range("A1:B100");
range1.Value = "Fixed";
And for copying you can try what #mrtig has suggested.
After searching in Internet and trying some codes i exported data from Excel to Datatable with Interop. The Problem is, it's very slow. Can someone give me a key how can i make it quicker with Interop, not OLEDB or anything else?
My code:
object misValue = System.Reflection.Missing.Value;
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(userSelectedFilePath2);
Excel._Worksheet xlWorksheet1 = xlWorkbook.Sheets[1];
Excel.Range xlRange1 = xlWorksheet1.UsedRange;
DataTable excelTb1 = new DataTable();
for (int j = 1; j <= xlRange1.Columns.Count; j++) // Header Names
{
excelTb1.Columns.Add(xlRange1.Cells[1, j].Value2.ToString());
}
DataRow dataRow = null;
for (int row = 2; row < xlRange1.Rows.Count + 1; row++)
{
dataRow = excelTb1.NewRow();
for (int col = 1; col <= xlRange1.Columns.Count; col++)
{
dataRow[col - 1] = (xlRange1.Cells[row, col] as Excel.Range).Value2;
}
excelTb1.Rows.Add(dataRow);
}
xlWorkbook.Close(true, misValue, misValue);
xlApp.Quit();
dataGridView1.DataSource = excelTb1;
I'll give you an answer to a question you didn't ask. Use NPOI library.
it will be faster
you won't have problems with forgetting to close your resources
Excel will not be required or used in the background
Here's the relevant code for that: NPOI : How To Read File using NPOI . For xlsx formats, use XSSFWorkbook instead (it is available starting from version 2.0).
My First thoughts on this are that you are looping through the Excel sheet and converting values one-by-one int your array to populate your structure.
Try something more along the lines of (Forgive my VB, but i'm sure oyu'll understand what I'm recommending):
Dim SpreadsheetVals(,) as object
SpreadhseetVals = xlWorksheet1.UsedRange
Then do your looping through your array instead.
That should improve speed considerably.
I am currently working in a C# application which has a class which will generate an excel file. Everything went smooth. The data populated on the excel sheet has 'Times New Roman' has font. I would like to change it to some other fonts (Calibari). How can I do that programmatically.
From what I tried, simply changing font name, size etc... on range changes font for that range:
range.Font.Name = "Arial"
range.Font.Size = 10
range.Font.Bold = true
Here is how:
//Declare Excel Interop variables
Microsoft.Office.Interop.Excel.Application xlApp;
Microsoft.Office.Interop.Excel.Workbook xlWorkBook;
Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet;
//Initialize variables
xlApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
//Set global attributes
xlApp.StandardFont = "Arial Narrow";
xlApp.StandardFontSize = 10;
Focus on the 2nd line from the bottom. That sets the default font type, but I wanted to show you where xlApp came from, even if it's self explanatory.
the following worked for me, when I tried setting the default application font it did nothing so I was able to set the font name of the active sheet rows and it worked. Also worth noting I used and tested this using Excel Interop version 12
Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
//Create\Add workbook object
Excel.Workbooks workBooks = excelApp.Workbooks;
//Excel.Workbook
Excel.Workbook workBook = workBooks.Add(Excel.XlWBATemplate.xlWBATWorksheet);
//use worksheet object
Excel.Worksheet workSheet = (Excel.Worksheet)excelApp.ActiveSheet;
//set default font
workSheet.Rows.Font.Name = "Arial";
Have you tried something like this:
new Font("Arial", 10, FontStyle.Bold);
var range = worksheet.get_Range(string.Format("{0}:{0}", startRowIndex, Type.Missing));
range = range.EntireRow;
range.Style.Font.Name = "Arial";
range.Style.Font.Bold = false;
range.Style.Font.Size = 12;
Hey Do not upset I do it and works for me .
Just define Font.Name and excell sheet fill all sheet use everywhere .
Any Way Code is :
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount-1]].Merge();
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount - 1]].Interior.Color = ColorTranslator.ToOle(Color.FromArgb(23,65,59));
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount - 1]].Borders.LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlContinuous;
workSheet.Range[workSheet.Cells[1, tempCount], workSheet.Cells[1, tempCount + mergeCount - 1]].Style.Font.Name = "Arial Narrow";
((Excel.Range)WorksheetResult.UsedRange).Font.Name = "Avant Garde";
WorksheetResult is just a sheet reference.
Found this thread by my own similar problem. I had a little picker box that, when a cell was clicked, needed to paste a unique font's symbol into the selected excel cell. Here's how i did that:
string selectedItem = arrayOfSymbols[tableLayoutPanel1.GetRow((Control)sender), tableLayoutPanel1.GetColumn((Control)sender)];
Excel.Worksheet ws = Globals.ThisAddIn.Application.ActiveSheet;
Excel.Range cell = Globals.ThisAddIn.Application.ActiveCell;
ws.Cells[cell.Row, cell.Column].Font.Name = "My Custom Font";
ws.Cells[cell.Row, cell.Column] = selectedItem;