I have a question. Is there a way that I could go through all the cols/rows in a spreadsheet using a for loop?? Right now I am using foreach loops like this in my code: (You can just ignore what's going on inside).
foreach (ExcelRow row in w1.Rows)
{
foreach (ExcelCell cell in row.AllocatedCells)
{
Console.Write("row: {0}", globalVar.iRowActual);
if (globalVar.iRowActual > 1)
{
cellValue = SafeCellValue(cell);
Console.WriteLine("value is: {0}", cellValue);
}
}
globalVar.iRowActual++;
}
The problem is that I would like to assign the value of each cell to a new variable and pass it to another method. I would like to use for loops for this and I know I can use CalculateMaxUsedColumns as the limit for the cols but is there a property like that, that I could use for the rows?!
This is what I would like to do:
int columnCount = ws.CalculateMaxUsedColumns();
int rowCount = ws.CalculateMaxUsedRows(); ------> PART I NEED HELP WITH
for(int i=0; i <columnCount; i++){
for(int j = 0; j<rowCount; j++){
.....
}
}
Any kind of help would be greatly appreciated. Thanks!!!
Here is a way you can iterate in GemBox.Spreadsheet through all the columns / rows in a spreadsheet using a for loop.
Go through the CellRange which is returned by ExcelWorksheet.GetUsedCellRange method.
ExcelFile workbook = ExcelFile.Load("Sample.xlsx");
ExcelWorksheet worksheet = workbook.Worksheets[0];
CellRange range = worksheet.GetUsedCellRange(true);
for (int r = range.FirstRowIndex; r <= range.LastRowIndex; r++)
{
for (int c = range.FirstColumnIndex; c <= range.LastColumnIndex; c++)
{
ExcelCell cell = range[r - range.FirstRowIndex, c - range.FirstColumnIndex];
string cellName = CellRange.RowColumnToPosition(r, c);
string cellRow = ExcelRowCollection.RowIndexToName(r);
string cellColumn = ExcelColumnCollection.ColumnIndexToName(c);
Console.WriteLine(string.Format("Cell name: {1}{0}Cell row: {2}{0}Cell column: {3}{0}Cell value: {4}{0}",
Environment.NewLine, cellName, cellRow, cellColumn, (cell.Value) ?? "Empty"));
}
}
EDIT
In newer versions there are some additional APIs which can simplify this. For instance, you can now use foreach and still retreive the row and column indexes with ExcelCell.Row.Index and ExcelCell.Column.Index and you can retreive the names without using those static methods (without RowColumnToPosition, RowIndexToName and ColumnIndexToName).
ExcelFile workbook = ExcelFile.Load("Sample.xlsx");
ExcelWorksheet worksheet = workbook.Worksheets[0];
foreach (ExcelRow row in worksheet.Rows)
{
foreach (ExcelCell cell in row.AllocatedCells)
{
Console.WriteLine($"Cell value: {cell.Value ?? "Empty"}");
Console.WriteLine($"Cell name: {cell.Name}");
Console.WriteLine($"Row index: {cell.Row.Index}");
Console.WriteLine($"Row name: {cell.Row.Name}");
Console.WriteLine($"Column index: {cell.Column.Index}");
Console.WriteLine($"Column name: {cell.Column.Name}");
Console.WriteLine();
}
}
Also, here are two other ways how you can iterate through sheet cells in for loop.
1) Use ExcelWorksheets.Rows.Count and ExcelWorksheets.CalculateMaxUsedColumns() to get the last used row and column.
ExcelFile workbook = ExcelFile.Load("Sample.xlsx");
ExcelWorksheet worksheet = workbook.Worksheets[0];
int rowCount = worksheet.Rows.Count;
int columnCount = worksheet.CalculateMaxUsedColumns();
for (int r = 0; r < rowCount; r++)
{
for (int c = 0; c < columnCount; c++)
{
ExcelCell cell = worksheet.Cells[r, c];
Console.WriteLine($"Cell value: {cell.Value ?? "Empty"}");
Console.WriteLine($"Cell name: {cell.Name}");
Console.WriteLine($"Row name: {cell.Row.Name}");
Console.WriteLine($"Column name: {cell.Column.Name}");
Console.WriteLine();
}
}
If you have a non-uniform spreadsheet in which rows have different column count (for instance, first row has 10 cells, second row has 100 cells, etc.), then you could use the following change in order to avoid iterating through non-allocated cells:
int rowCount = worksheet.Rows.Count;
for (int r = 0; r < rowCount; r++)
{
ExcelRow row = worksheet.Rows[r];
int columnCount = row.AllocatedCells.Count;
for (int c = 0; c < columnCount; c++)
{
ExcelCell cell = row.Cells[c];
// ...
}
}
2) Use CellRange.GetReadEnumerator method, it iterates through only already allocated cells in the range.
ExcelFile workbook = ExcelFile.Load("Sample.xlsx");
ExcelWorksheet worksheet = workbook.Worksheets[0];
CellRangeEnumerator enumerator = worksheet.Cells.GetReadEnumerator();
while (enumerator.MoveNext())
{
ExcelCell cell = enumerator.Current;
Console.WriteLine($"Cell value: {cell.Value ?? "Empty"}");
Console.WriteLine($"Cell name: {cell.Name}");
Console.WriteLine($"Row name: {cell.Row.Name}");
Console.WriteLine($"Column name: {cell.Column.Name}");
Console.WriteLine();
}
Related
I created Excel file using this code:
Sheets worksheets = wb.Sheets;
Worksheet worksheet = (Worksheet)worksheets[4];
int rows = dt.Rows.Count;
int columns = dt.Columns.Count;
var data = new object[rows + 1, columns];
for (var column = 0; column < columns; column++)
{
data[0, column] = dt.Columns[column].ColumnName;
}
for (var row = 0; row < rows; row++)
{
for (var column = 0; column < columns; column++)
{
data[row + 1, column] = dt.Rows[row][column];
}
}
Range beginWrite = (Range)worksheet.Cells[1, 1];
Range endWrite = (Range)worksheet.Cells[rows + 1, columns];
Range sheetData = worksheet.Range[beginWrite, endWrite];
sheetData.Value2 = data;
worksheet.Select();
sheetData.Worksheet.ListObjects.Add(XlListObjectSourceType.xlSrcRange,
sheetData,
Type.Missing,
XlYesNoGuess.xlNo,
Type.Missing);
sheetData.Select();
Excel.ActiveWindow.DisplayGridlines = false;
Excel.Application.Range["2:2"].Select();
Excel.Application.Range["$A$3"].Select();
the problem here it set default format style to excel fileI don't know how to clear all format style in excel sheet
If all you are trying to do is delete all styles, this would work:
using Excelx = Microsoft.Office.Interop.Excel;
Excelx.Workbook wb = excel.ActiveWorkbook;
foreach (Excelx.Style st in wb.Styles)
st.Delete();
Then again, you may only want to clear out custom styles (not the ones that come standard), in which case a small modification would do it:
foreach (Excelx.Style st in wb.Styles)
{
if (!st.BuiltIn)
st.Delete();
}
Styles are stored at the workbook level, so at some point you need to declare your workbook. From there, the Styles collection of the Workbook object has everything you need.
I am trying to export a database from c# to excel but the first row from the database is not saving in excel.
private void exporttoexcel()
{
Microsoft.Office.Interop.Excel._Application excel = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel._Workbook workbook = excel.Workbooks.Add(Type.Missing);
Microsoft.Office.Interop.Excel._Worksheet worksheet = null;
try
{
worksheet = workbook.ActiveSheet;
worksheet.Name = "ExportedFromDatGrid";
int cellRowIndex = 1;
int cellColumnIndex = 1;
//Loop through each row and read value from each column.
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
for (int j = 0; j < dataGridView1.Columns.Count; j++)
{
// Excel index starts from 1,1. As first Row would have the Column headers, adding a condition check.
if (cellRowIndex == 1)
{
worksheet.Cells[cellRowIndex, cellColumnIndex] = dataGridView1.Columns[j].HeaderText;
}
else
{
worksheet.Cells[cellRowIndex, cellColumnIndex] = dataGridView1.Rows[i].Cells[j].Value.ToString();
}
cellColumnIndex++;
}
cellColumnIndex = 1;
cellRowIndex++;
}
}
catch(Exception ex)
{
}
}
here is the code I'm using. could anyone help me ? I am new in coding.
You're not writing out the data but are only writing out column names when the cellColumnIndex is 1, skipping the first row. But after the first row has been processed, the row index will be incremented. Refactor your for-loop to look something like this:
// Add the column names
var index = 0;
foreach(var column in dataGridView1.Columns)
{
worksheet.Cells[0, index] = column.HeaderText;
index++;
}
//Loop through each row and read value from each column.
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
for (int j = 0; j < dataGridView1.Columns.Count; j++)
{
// Excel index starts from 1,1. As first Row would have the Column headers, adding a condition check.
worksheet.Cells[cellRowIndex, cellColumnIndex] = dataGridView1.Rows[i].Cells[j].Value.ToString();
cellColumnIndex++;
}
cellColumnIndex = 1;
cellRowIndex++;
}
Please have a look at ClosedXML. It simplifies writing your code, and eliminate the need to have Excel installed on the machine where you want to run this.
I have a datatable filled with information from an excel file. I have more than four columns but to bring an example I'm writing just four of them. I have to write a program in which if the value of the cell in the column C is 0, then I have to copy column B to column A. If the value of the cell in column C is > 0 then i have to copy the column B to A and should add another row in which i have to copy the value of the column C to A.
What i have till now is
for (int r = 2; r <= ws.UsedRange.Rows.Count; r++)
{ if (ws.UsedRange.Cells[r, 3].Text == "0")
{
DataRow row = dt.NewRow();
for (int c = 1; c < ws.UsedRange.Columns.Count; c++)
{
string cell = ws.Cells[r, c].Text;
row[c - 1] = cell;
}
}
So my questions are:
How can i copy a column to another in the same datatable? Copy B to A.
How can i add another row and copy the value of C to A only for that row?
Here is the full code:
public DataTable ReadExcel2(string file)
{
ExcelI.Application app = new ExcelI.Application(); //create an excel instance
ExcelI.Workbook wb = app.Workbooks.Open(file, ReadOnly: true); //open a file
ExcelI.Worksheet ws = wb.Worksheets[1]; //choose a sheet. The firt one
var rng = ws.UsedRange;
//takes the index of the columns that are going to be filtered
int service = ColumnIndexByName(ws.Cells[1, 1].EntireRow, "Service");
int status = ColumnIndexByName(ws.Cells[1, 1].EntireRow, "Status");
int code = ColumnIndexByName(ws.Cells[1, 1].EntireRow, "Code");
DataTable dt = new DataTable();
dt.Columns.Add("A", typeof(string));
for (int c = 1; c < ws.UsedRange.Columns.Count; c++)
{
string colName = ws.Cells[1, c].Text;
int i = 2;
while (dt.Columns.Contains(colName))
{
colName = ws.Cells[1, c].Text + "{" + i.ToString() + "}";
i++;
}
dt.Columns.Add(colName);
}
//do a loop to delete the rows that we dont need
for (int r = 2; r <= ws.UsedRange.Rows.Count; r++)
{
if (ws.UsedRange.Cells[r, 3].Text == "0")
{
DataRow row = dt.NewRow();
for (int c = 1; c < ws.UsedRange.Columns.Count; c++)
{
string cell = ws.Cells[r, c].Text;
row[c - 1] = cell;
}
dt.Rows.Add(row);
row["A"] = row["C"];
}
}
//Close the file
wb.Close();
//release the excel objects from use
Marshal.ReleaseComObject(wb);
Marshal.ReleaseComObject(ws);
//take the id of excel process
int pid = app.PID();
app.Quit();
StartProc("taskkill", $"/f /pid {pid}");
return dt;
}
To add row use dt.Rows.Add(row);, about "copy the column B to A" you mean copy value , just assign row[0] = row[2];, by the way , your example missing a bracket.
I think you should review your code according to conditions in your question, and you can do it yourself as well. Just pay attention to condition you wrote in question and conditional operator you checked in the code.
I have a DataTable with size m x n and want to copy over all the contents(including column headers) to an excel file that is already open. I have the reference to the Excel.WorkBook and it is known which WorkSheet will the data be copied to.
I know the easiest(and dirtiest way) is:
Excel.WorkSheet outSheet; //set to desired worksheet
int rowIdx = 1;
int colIdx = 1;
//add header row
foreach (DataColumn dc in dt.Columns)
{
outSheet.Cells[rowIdx, colIdx++] = dc.ColumnName;
}
colIdx = 1; //reset to Cell 1
//add rest of rows
foreach (DataRow dr in dt.Rows)
{
colIdx = 0;
foreach (DataColumn dc in dt.Columns)
{
outSheet.Cells[rowIdx + 1, colIdx + 1] = dr[colIdx].ToString();
colIdx++;
}
rowIdx++;
}
This works but unfortunately incurs a huge time cost as it needs to access and paste data cell by cell. Is there a better way to accomplish this?
I wrote a small example for you. tl;dr you can assign an array of values to an Excel range. But this one must meet some specifications. credits go to Eric Carter
Stopwatch sw = new Stopwatch();
sw.Start();
Application xlApp = new Application();
Workbook xlBook = xlApp.Workbooks.Open(#"E:\Temp\StackOverflow\COM_Interop_CS\bin\Debug\demo.xlsx");
Worksheet wrkSheet = xlBook.Worksheets[1];
try
{
/// credits go to:
/// http://blogs.msdn.com/b/eric_carter/archive/2004/05/04/126190.aspx
///
/// [cite] when you want to set a range of values to an array, you must declare that array as a 2
/// dimensional array where the left-most dimension is the number of rows you are going to set and
/// the right-most dimension is the number of columns you are going to set.
///
/// Even if you are just setting one column, you can’t create a 1 dimensional array and have it work[/cite]
Excel.Range range = wrkSheet.Range["A1", "Z100000"];
int maxRows = 100000, maxCols = 26;
object[,] values = new object[maxRows, maxCols];
int counter = 0;
for (int row = 0; row < maxRows; row++)
{
for (int col = 0; col < maxCols; col++)
{
values[row, col] = counter++;
}
}
range.Value2 = values;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
xlApp.Visible = true;
sw.Stop();
Console.WriteLine("Elapsed: {0}", sw.Elapsed);
I added 100.000 rows and 26 cols in less than 10 seconds. I hope this is appropriate for you!
A rather higeisch dataset with 16000 x 12 entries needs to be dumped into a worksheet.
I use the following function now:
for (int r = 0; r < dt.Rows.Count; ++r)
{
for (int c = 0; c < dt.Columns.Count; ++c)
{
worksheet.Cells[c + 1][r + 1] = dt.Rows[r][c].ToString();
}
}
I rediced the example to the center piece
Here is what i implemented after reading the suggestion from Dave Zych.
This works great.
private static void AppendWorkSheet(Excel.Workbook workbook, DataSet data, String tableName)
{
Excel.Worksheet worksheet;
if (UsedSheets == 0) worksheet = workbook.Worksheets[1];
else worksheet = workbook.Worksheets.Add();
UsedSheets++;
DataTable dt = data.Tables[0];
var valuesArray = new object[dt.Rows.Count, dt.Columns.Count];
for (int r = 0; r < dt.Rows.Count; ++r)
{
for (int c = 0; c < dt.Columns.Count; ++c)
{
valuesArray[r, c] = dt.Rows[r][c].ToString();
}
}
Excel.Range c1 = (Excel.Range)worksheet.Cells[1, 1];
Excel.Range c2 = (Excel.Range)worksheet.Cells[dt.Rows.Count, dt.Columns.Count];
Excel.Range range = worksheet.get_Range(c1, c2);
range.Cells.Value2 = valuesArray;
worksheet.Name = tableName;
}
Build a 2D array of your values from your DataSet, and then you can set a range of values in Excel to the values of the array.
object valuesArray = new object[dataTable.Rows.Count, dataTable.Columns.Count];
for(int i = 0; i < dt.Rows.Count; i++)
{
//If you know the number of columns you have, you can specify them this way
//Otherwise use an inner for loop on columns
valuesArray[i, 0] = dt.Rows[i]["ColumnName"].ToString();
valuesArray[i, 1] = dt.Rows[i]["ColumnName2"].ToString();
...
}
//Calculate the second column value by the number of columns in your dataset
//"O" is just an example in this case
//Also note: Excel is 1 based index
var sheetRange = worksheet.get_Range("A2:O2",
string.Format("A{0}:O{0}", dt.Rows.Count + 1));
sheetRange.Cells.Value2 = valuesArray;
This is much, much faster than looping and setting each cell individually. If you're setting each cell individually, you have to talk to Excel through COM (for lack of a better phrase) for each cell (which in your case is ~192,000 times), which is incredibly slow. Looping, building your array and only talking to Excel once removes much of that overhead.