Cell from ExcelRange - c#

This problem has me completely puzzled.
I have a Excel document which loads in just fine.
It has rows, columns and data and I want to iterate through the rows.
But EPPLus is odd.
I take the second row:
ExcelRange range1 = worksheet.Cells[2, worksheet.Dimension.Start.Column, 2, worksheet.Dimension.End.Column];
Which gives me {A2:D2} Splendid! so far so good but then I want the first cell of the row:
ExcelRange range2 = range1[1,1];
Which give me {A1} and to make matter worse, the value of range1 has also changed to {A1} instead of the row I selected.
How can I resolve this issue and take a ExcelRange from an ExcelRange?
This has me completely puzzled .... thanks for anyhelp

I did have the same problem, to get the correct start cell:
var range2 = worksheet.Cells[range1.Start.Row, range1.Start.Column];
And the same for the bottom right cell:
var range3 = worksheet.Cells[range1.End.Row, range1.End.Column];

If you look at the code behind the ExcelRange Indexer you will see that the get will actually set the base address (the nested else):
public ExcelRange this[string Address]
{
get
{
if (_worksheet.Names.ContainsKey(Address))
{
if (_worksheet.Names[Address].IsName)
{
return null;
}
else
{
base.Address = _worksheet.Names[Address].Address;
}
}
else
{
base.Address = Address;
}
_rtc = null;
return this;
}
}
Why they did it this way I am not sure (I assume there its an implementation detail). But that would explain why referencing another address changes the selected range. So, like Benexx said, have to do a direct reference from the Cells collection of the Worksheet.

You can use the Offset method to get a sub-range of an ExcelRange. This give you a new instance of ExcelRange and does not modify the original instance.
https://www.epplussoftware.com/docs/5.5/api/OfficeOpenXml.ExcelRangeBase.html#OfficeOpenXml_ExcelRangeBase_Offset_System_Int32_System_Int32_System_Int32_System_Int32_
Here is an example:
public static void AlternateRowColor(ExcelRange range)
{
for (var rowOffset = 1; rowOffset < range.Rows; rowOffset += 2)
{
using (var rowRange = range.Offset(rowOffset, 0, 1, range.Columns))
{
rowRange.Style.Fill.PatternType = ExcelFillStyle.Solid;
rowRange.Style.Fill.BackgroundColor.SetColor(Color.Cornsilk);
}
}
}

Related

How to know editable cell address in merged cells

enter image description here
Here, there are four cells(A1,B1,C1,D1) have been merged, I would like to get the editable cell address(A1).
According to Excel A1 Cell able to edit, others not editable.
I could able to get merged cells count as 4 using below code.
Also I can get whether cell has been merged or not.
But not able to get the address of editable cell(A1) in merged cells.
Microsoft.Office.Interop.Excel.Application appl = null;
appl = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook wbk;
wbk = appl.Workbooks.Open("C:\\Users\\test\\Desktop\\test.xlsx");
Worksheet sheet = wbk.Worksheets.get_Item(1);
Range range = sheet.Cells[1, 2]; //B1 Cell
var a = range.Cells.MergeArea.Count; // can be able to get merged count cells
string b = range.Address;
**int ab = range.Cells.Areas.Count;**
Range ac = range.Cells.Areas.get_Item(1);
for (int i = 1; i <= 4; i++)
{
**if (sheet.Cells[1, i].Mergecells)** //merged cell
{
MessageBox.Show("Merged");
}
else //Unmerged cell
{
MessageBox.Show("UnMerged Cells");
}
}
wbk.Save();
wbk.Close(false);
appl.Quit();
The MergeArea property of a Range is again a Range. If you have a cell, just use something like the following to get the first (=top left) cell of the merged area (untested in C# as I don't have it available, but works in VBA and should also work for C#):
Range range = sheet.Cells[1, 2];
Range firstCell = range.MergeArea.Cells[1, 1];
This works even if a cell is not merged, MergeArea is always set, so on an unmerged cell range and firstCell would point to the same cell.
If I correctly understood your real need, please try the next approach. It is based on the fact that a merged area keeps the value in its first cell:
'your existing code...
if (sheet.Cells[1, i].Mergecells) //merged cell
{
MessageBox.Show(sheet.Cells[1, i].MergeArea.cells[1, 1].Value);
}
else //Unmerged cell
{
MessageBox.Show(sheet.Cells[1, i].Value);
}
'your existing code...
Here is the solution
string mergedCellsAddress = mergedRange.Address;
string[] firstMergedCell = mergedCellsAddress.Split(':');
string firstCell = firstMergedCell.GetValue(0).ToString().Replace("$", "");

Copy/Paste cells & value with property

I want to copy/paste range at worksheet including the values/property(Strikethrough) in the cells to another new workbook.
I can't set this property at new sheet. Using Microsoft.Office.Interop.Excel; I cant copy this property like in the picture. How can I do it. 3 line in 1 cell with different property
public void WriteCellWithFont(int i, int j , _Excel.Range cell)
{
i++;
j++;
ws.Cells[i, j].Value2 = cell.Value2;
ws.Cells[i,j].Font.Strikethrough = true;
}
Try with PasteSpecial. Its like normal way of we use to keep formatting when paste.
// copy
Range cells1 = (Range)worksheet1.Cells[2, 3];
cells1.Copy();
// Paste
Range cells2= (Range)worksheet2.Cells[2, 3];
cells2.PasteSpecial(XlPasteType.xlPasteFormats);

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.

C# iterate over a row in Excel

I need to iterate over a specific excel row. For now I've got a code to iterate over a column and I want it to be similar to that. It looks like this:
int columnLength = xlWorkSheet.UsedRange.Rows.Count;
string lastCell = Regex.Replace(CONST_FIRST_CELL, #"\d+", columnLength.ToString()); //will give me the last cell in the column
var excelColumn = xlWorkSheet.Range[CONST_FIRST_CELL, lastCell ];
if (excelColumn == null)
{
throw new Exception("bad col");
}
for (int i = 1; i < columnLength ; i++)
{
Excel.Range currentValue = excelColumn [i];
....DO SOME STUFF....
}
how can I iterate over a specific row? I'm not sure how to get the last column like I got the last row cell in the above implementation since then I just had to switch a number with the length but now I somehow need to get the correct last cell of a row (which means switching the letters somehow i.e C4 to AD4) in order to get the range of first cell row and last...
The best solution I guess involves a foreach loop somehow?
You were almost there, your loop just needs some tuning:
//Input all the Int values you want
int targetRow = 1;
int startCol = 1;
int maxCol = 10; //With this value the loop below will iterate until column 9 (inclusive)
for (int i = startCol; i < maxCol ; i++)
{
Excel.Range currentRange = (Excel.Range)xlWorkSheet.Cells[targetRow, i];
if (currentRange.Value2 != null)
{
string curVal = currentRange.Value2.ToString();
}
}
IMO this is the best way to iterate through cells (by considering rows and/or columns). You can do it differently (on the lines of what you were trying): iterating within the columns of a given range (you would need to define the range as Excel.Range Type and then rely on the in-built property Columns), although I think that this can be more confusing. Example: if you have as input range C1:H5, "C:C" is the first column, "D:D" the second column, etc. With my approach, the first column will always be "A:A", the second column "B:B", etc.
Example of iterating through columns in a given range (inputRange):
foreach(Excel.Range curCol in inputRange.Columns)
{
if (curCol.Value2 != null)
{
//As far as each column only has one row, each column can be associated with a cell
string curVal = curCol.Value2.ToString();
}
}

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;
}
}

Categories