"Column out of Range" Error when populating an Excel Sheet - c#

I just started implementing this to populate an Excel sheet with some data:
using OfficeOpenXml;
//..
ExcelWorksheet VerificationSheet_Sheet4 = package.Workbook.Worksheets.Add("SheetTitleHere");
int row = 0, col = 0;
VerificationSheet_Sheet4.Cells[row + 1, col].Value = "AnyStringHere"; // error here
However it pops an error saying column is out of range. Why and how can I fix that?

Excel worksheets use 1 based indexing rather than zero based. Thus columns and rows both start at 1 and not 0.

As per the elaboration by #Darren Young Excel uses 1 based indexing thats where the issue is.
using OfficeOpenXml;
//..
ExcelWorksheet VerificationSheet_Sheet4 = package.Workbook.Worksheets.Add("SheetTitleHere");
int row = 1, col = 1;
VerificationSheet_Sheet4.Cells[row + 1, col].Value = "AnyStringHere";

Related

Validate column with Excel formula increments the formula is expected behavior or bug?

I'm trying to create a spreadsheet where the first sheet ("Catalog") contains some pre-filled and some empty values in a column. I want the values to be in a drop down list that are restricted to values found in the second sheet ("Products").
I would expect that if I set the the Excel validation formula for cells "A1:A1048576" in the "Catalog" sheet to be a list validation of "Products!A1:A100" that every cell would only allow values from "Products!A1:A100". However, I'm finding that my formula gets incremented for every row in the "Catalog" sheet (i.e. In row 2 the formula becomes "Products!A2:A101", in row 3 the formula becomes "Products!A3:A102").
If version matters I'm using EPPlus.Core v1.5.4 from NuGet.
I'm not sure if this is a bug or if I'm going about applying my formula wrong?
I've already tried directly applying the validation to every cell in the column one cell at a time. I found that not only does it moderately increase the size of the resulting Excel file but more importantly it also exponentially increases the time taken to generate the Excel file. Even applying the validation one cell at a time on the first 2000 rows more than doubles the generation time.
ExcelPackage package = new ExcelPackage();
int catalogProductCount = 10;
int productCount = 100;
var catalogWorksheet = package.Workbook.Worksheets.Add($"Catalog");
for (int i = 1; i <= catalogProductCount; i++)
{
catalogWorksheet.Cells[i, 1].Value = $"Product {i}";
}
var productsWorksheet = package.Workbook.Worksheets.Add($"Products");
for (int i = 1; i <= productCount; i++)
{
productsWorksheet.Cells[i, 1].Value = $"Product {i}";
}
var productValidation = catalogWorksheet.DataValidations.AddListValidation($"A1:A1048576");
productValidation.ErrorStyle = ExcelDataValidationWarningStyle.stop;
productValidation.ErrorTitle = "An invalid product was entered";
productValidation.Error = "Select a product from the list";
productValidation.ShowErrorMessage = true;
productValidation.Formula.ExcelFormula = $"Products!A1:A{productCount}";
I guess I'm not that adept at Excel formulas.
Changing this line:
productValidation.Formula.ExcelFormula = $"Products!A1:A{productCount}";
to this:
productValidation.Formula.ExcelFormula = $"Products!$A$1:$A${productCount}";
stopped the auto increment issue. Hopefully this answer will save someone else some sanity as I wasted half a day on this issue myself.

How can I evaluate the no. of records in a range of a single excel column in c#?

I am trying an automation work on an excel sheet using c#. I want to delete blank rows present at the end of the excel sheet based on the no. of records in the first column only. Any other column may have different no. of rows than that in the first column.
For this purpose first I tried to evaluate the range of the first column and based on that I tried to delete rows after this in the excel sheet.
I used the following lines of code for this purpose :-
Range lastrow_new = sourceSheet2.get_Range("A6", Type.Missing);
if (excelApp.WorksheetFunction.CountA((int)(sourceSheet2.Rows[lastrow_new])) == 0)
{
for (int i = 1; i <= (int)(sourceSheet2.Rows[lastrow_new]); i++)
{
if (excelApp.WorksheetFunction.CountA(sourceSheet2.Rows[i]) == 0)
{
Range BlankRows = sourceSheet2.get_Range("A" + i + ":" + "FN" + lastrow_new);
BlankRows.Select();
BlankRows.EntireRow.Delete();
i = (int)(sourceSheet2.Rows[lastrow_new]) + 1;
}
}
}
But it is giving error of 'Type mismatch'.
Can anyone help ?
Thanks in advance.
I'm not sure i understand what you want... but if you want clear the consecutive rows after 'A6' column, you can try this code:
string value = sourceSheet2.Range["A1"].SpecialCells(XlCellType.xlCellTypeLastCell).Address;
value = value.Replace("$","");
sourceSheet2.Range["A6", value].ClearContents();

EPPlus - Named Range is there but not working

Similar to
EPPlus - Named Range is not populated
In his case, his ranges were at the workbook level but he was looking at the worksheet level.
My EP code shows a count of 0 ranges at the workbook level and 15 at the sheet level, as it should be.
Opening the worksheet.Names shows all 15, with proper names.
Retrieve a range, and the formula is correct with
"OFFSET(Sheet1!$A$33, 0, Sheet1!_CurrentMonth, 1, 55 -Sheet1!_CurrentMonth)", but almost everything else returns an exception on evaluation.
It reports 1 column, which is incorrect.
And the 'FullAddress' looks correct with "'Sheet1'!_Fund1Projected", but 'FullAddressAbsolute' gives "$#REF!$-1"
Lastly, I'm using a template, xltm, to create a spreadsheet, xlsm.
public static void CreateChart()
{
var excelFullPath = "C:\\Users\\username\\Documents\\Excel\\Templates\\";
var excelFileName = "LowCashBalanceChart.xlsm";
FileInfo newFile = new FileInfo(excelFullPath + excelFileName);
if (newFile.Exists)
newFile.Delete();
FileInfo template = new FileInfo(excelFullPath + "Sample Chart.xltm");
using (ExcelPackage xlPackage = new ExcelPackage(newFile, template))
{
ExcelWorksheet worksheet = xlPackage.Workbook.Worksheets["Sheet1"]; //xlPackage.Workbook.Worksheets.FirstOrDefault();
ExcelNamedRange namedRange;
namedRange = xlPackage.Workbook.Names["_Fund1Projected"]; // fails, no ranges at the WB level
namedRange = worksheet.Names["_Fund1Projected"]; // this one works
for (int rowIndex = namedRange.Start.Row; rowIndex <= namedRange.End.Row; rowIndex++) // Exception on range.Start
// 'namedRange.Start' threw an exception of type 'System.ArgumentOutOfRangeException'
{
for (int columnIndex = namedRange.Start.Column; columnIndex <= namedRange.End.Column; columnIndex++)
{
worksheet.Cells[rowIndex, columnIndex].Value = (rowIndex * 100 + columnIndex).ToString();
}
}
xlPackage.Save();
}
}
I looked at the code on GitHub, but nothing stands out.
Tried it with the ranges at the workbook level as well with the same results.
I solved my problem, I'll put answer here for anyone that may need it in the future.
I created a range for a 3x3 square.
Range1 = =Sheet1!$A$24:$C$26
I can write to that just fine. No exceptions.
But when we have ranges that have endpoints determined by values of other cells, it fails.
=OFFSET(Sheet1!$A$32, 0, Sheet1!_CurrentMonth, 1, 55 -Sheet1!_CurrentMonth)
The problem is that our named ranges are dynamic.
That’s why it was getting an exception.
The work-around is to not use dynamic ranges from EPPlus.
Just a little more C# code to handle the dynamic part instead of Excel handling it for you.

Read Excel Cell And Set Row Colour

I am using Com Interop and C#. I have to iterate through an Excel file looking for certain values in each of the rows (always in column 2). For some values I need to set the background colour of the row to red.
I am having trouble:
Reading the value in cell [i][2] for row i, and
Setting the background colour of this row.
Basically I am looking for something like this (which is the best I can find after much Googling):
// ws is the worksheet
for (int i = 1; i <= ws.Rows.Count; i++)
{
Range range = ws.Cells[i][2];
int count = Convert.ToInt32(range.Value2.ToString());
if (count >= 3)
{
Range chronic = ws.UsedRange.Rows[i];
chronic.EntireRow.Cells.Interior.Color = 0xFF0000;
}
}
Of course this doesn't work. I can't get past the first hurdle of just reading the cell. Any advice is appreciated.
Try this. The code assumes that the value in the column 2 cell is a number.
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
Missing noValue = Missing.Value;
Excel.Range conditionalCell;
foreach (Excel.Range usedRange in ws.UsedRange.Rows)
{
conditionalCell = usedRange.Cells[noValue, 2] as Excel.Range;
if (Convert.ToInt32(conditionalCell.Value2) >= 3)
{
usedRange.Cells.Interior.Color = Excel.XlRgbColor.rgbRed;
}
}

.net c# excel column AutoFit

I'm working with an excel object in c#. I want to auto-fit the columns, but like this: I want the columns' width to be 5 bigger than what the AutoFit method set.
How can I get the width after AutoFit() is used?
How can I make the columns 5 bigger than this width?
If you wish to use the Selection object and have IntelliSense with early binding, you need to cast the Selection object to a Range first:
Excel.Range selectedRange = (Excel.Range)myExcelApp.Selection;
selectedRange.Columns.AutoFit();
foreach (Excel.Range column in selectedRange.Columns)
{
column.ColumnWidth = (double)column.ColumnWidth + 5;
}
-- Mike
Assuming that you are on cell A1 & have long text in it, following code will make the column Autofit and then increase the width by 5 characters.
Selection.Columns.Autofit
Selection.Columns(1).ColumnWidth = Selection.Columns(1).ColumnWidth + 5
Try to loop through your rows to get the text length of it:
var row = 1;
ws.Column(1).AutoFit(ws.Cells[row, 1].Text.Length + 5);
Where ws is your Worksheet:
var pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Plan1")
Try Like this,
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Sheet1");
//Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
ws.Cells["A1"].LoadFromDataTable(data_table, true);
//Set full Sheet Auto Fit
ws.Cells[1, 1, data_table.Rows.Count, data_table.Columns.Count].AutoFitColumns();

Categories