Apache POI - XSSF: Row.getCell() - apache-poi

I am using XSSF to access the .xlsx format. Extracting row data and cell data is being done by
Row.getCell(1) // to get the first cell data.
Is there a way to access cells like
Row.getCell(A) or Row.getCell(AC).
This will be very helpfull for me to access columns.
Can any one tell me the way to do this?

I think the main class you're looking for is CellReference - it handles converting between user facing references such as "B2" into file format references like row=1,col=1 . There's a static method on there that handles your exact use case, convertColStringToIndex
For your use case, you'd want code something like
Cell c = row.getCell( CellReference.convertColStringToIndex("G") );

The question is regarding reaching out a cell using its references, if I am not wrong.
Sheet referenceSheet = workbook.getsheet("Sheet name your intrested in");
CellReference ref = new CellReference("C24");
Row row = referenceSheet.getRow(ref.getRow());
// null check for the row
if(row != null)
{
Cell cell = row.getCell(ref.getCol());
}
In this way we can refer a cell using its reference.

Related

Adding complicated INDEX formula results in Excel file error

I have a problem inserting this formula in Excel spreadsheet using OpenXML SDK.
=INDEX(Codes[[#All];[code1:]];MATCH(MySheet!B1674;Codes[[#All];[code2:]];0))
I've been doing this just by adding text into a cell using method like this:
private Cell ConstructCellFormula(string formula)
{
Cell cell = new Cell();
CellFormula cFormula = new CellFormula();
cFormula.Text = formula;
cell.Append(cFormula);
return cell;
}
I know that I'm missing something in calling this.
The spreadsheet that I'm working on is a part of many in XLSM file.
Codes
is also there.
What I'm doing is - I remove all existing rows and add new based on data from the database. That's when problems with the file start.
Any help will be appreciated.
Solution was to simply use EN-US syntax with commas instead of semi-colons.
=INDEX(Codes[[#All],[code1:]],MATCH(MySheet!B1674,Codes[[#All],[code2:]],0))

Excel open xml sdk - Get an empty cell

I have excel file that I need to locate a cell at.
This cell might have attributes like formatting, data validations rules etc. but no value.
I need to retrieve it even if the cell has (currently) no value since I want to maintain all the cell attributes and only set it a value.
Is it possible?
The code:
public static Cell GetSpreadsheetCell(WorksheetPart worksheetPart, string addressName)
{
return worksheetPart.Worksheet.Descendants<Cell>().
Where(c => c.CellReference == addressName).FirstOrDefault();
}
It seems this is not a real a problem.
Data validation is a side object that store references to cells it applies to. When a formula is inserted the cell always contain some value...

can background color of a cell on an excel sheet be read using coldfusion

I have an excel sheet where additions have a red background, changes have a yellow background, and deletions are grey.
What I am hoping to do is read through the sheet, and based on the cell background color, perform the relevant database action.
Normally I would make each type of action in its own column, or add another column to determine action.
What options do I have for getting at the "format" that comes back in the spreadsheet object?
Thanks
Relying on cell color sounds brittle IMO. Assigning an explicit action column would be a better approach IMO.
That said, it is possible to access the color. However, there are no built in CF methods. You must dip into the underlying POI. First iterate through the cells in the spreadsheet:
<cfscript>
// get the sheet you want to read
cfSheet = SpreadSheetRead("c:/path/to/somefile.xlsx");
workbook = cfSheet.getWorkBook();
sheetIndex = workbook.getActiveSheetIndex();
sheet = workbook.getSheetAt( sheetIndex );
// process the rows and columns
rows = sheet.rowIterator();
while (rows.hasNext()) {
currentRow = rows.next();
// loop through populated cells in this row
cells = currentRow.cellIterator();
while (cells.hasNext()) {
currentCell = cells.next();
// .... get color
}
}
</cfscript>
Then for each cell, extract the style color. Not tested, but something like this should work. (See XSSFColor)
cellColor = currentCell.getCellStyle().getFillForegroundColorColor();
colorValue = cellColor.getARGBHex();
Update:
As #Sean mentioned in the comments, CF9 does not have the method above. Unfortunately getFillForegroundColorColor() and getARGBHex() were introduced sometime around 3.7, but CF is bundled with an earlier version: 3.5 (I think). So you must use the indexed color method instead (or upgrade the POI jars).
// only create once
colors = createObject("java", "org.apache.poi.ss.usermodel.IndexedColors");
//....
cellColor = currentCell.getCellStyle().getFillForegroundColor();
if (cellColor == colors.RED.getIndex()) {
WriteDump("This cell is RED. Do something...");
}

Excel VBA changing my formulas in a table?

Has anyone come across a situation where Excel seems to manipulate your formulas.
I have a sheet where I have an Index value in Column A. The First row starts with any non zero Value. Subsequent rows in the column increment the value. Eg
A1 = 1000
A2= A1+ 1
A3= A2 + 1
and so on/
I have another column B whose values will either be blank or a formula pointing to column A(usually the subsequent rows)
Eg:
B1.Formula = "=A2"
B2.Formula = "=A3"
B3.Value = ""
B4.value = "=A6"
Now I have a backup-restore functionality that lets me write out the data/formulas to a text file and then read it back in another workbook.
In the case of columns A and B, I am checking if the text value starts with "=" and then set either the value or formula of that cell depending on whether there is a formula or not.
So far the functionality has worked fine. It lets me restore accurately.
Now, if I convert this data range to a table and modify the code accordingly the behaviour is strange. I am using the ListObject structure to refer to the table. So for Column B my restore code is:
If Left(soureString) = "=" Then
'This is a formula
Sheets("MySheet").ListObjects(1).ListColumns("Next").DataBodyRange(row).Formula = sourcestring
Else
'This is a value
Sheets("MySheet").ListObjects(1).ListColumns("Next").DataBodyRange(row).Value = soureString
End If
once I am done writing a row, I loop to the start and
Dim newRow AS listrow
Set newRow = Sheets("MySheet").Listrows.Add(AlwaysInsert:=False)
row = newRow.Index
But this time when I run the process. this is what I get:
B1.Formula = "=A5"
B2.Formula = "=A5"
B3.Value = ""
B4.value = "=A5"
Why are my formula values all changing to the same value when I use a table instead of a range?
I had the same issue when populating a ListObject (Table) from an Excel Add-in, setting AutoFillFormulasInLists was the solution.
My workaround is to save the current setting, set AutoFillFormulasInLists to false, populate the table with data, formulas etc, then set AutoFillFormulasInLists back to the original setting.
bool OriginalAutoFillFormulaInListsFlag = app.AutoCorrect.AutoFillFormulasInLists;
app.AutoCorrect.AutoFillFormulasInLists = false;
//[ListObject population code....]
if (OriginalAutoFillFormulaInListsFlag == true)
{
app.AutoCorrect.AutoFillFormulasInLists = true;
}
Hope this helps someone.
I faced a similar issue. Ideally you could tell excel to stop doing this but I haven't been able to figure out how. Supposedly doing the following is supposed to keep excel from copying the formulas:
xlApp.AutoCorrect.AutoFillFormulasInLists = false
but it didn't work for me.
Using the answer from this question How to create running total using Excel table structured references? helped me. It doesn't feel like the ideal solution but it does do the job.
I used this formula where Weight is a column name from my table. #This Row is a "Special item specifier" and has a special meaning. The syntax looks a little funky because it's what's called a Structured Reference:
=AVERAGE(INDEX([Weight],1):[[#This Row],[Weight]])
The INDEX([Weight],1) part gives the reference for the 1st row in the Weight column
While the [[#This Row],[Weight]] part gives the reference for the current row in the Weight column.
So for example, if Weight is column J, and the current row is, say, 7 then this is equivalent to
=AVERAGE(J1:J7)
and on the 8th row it will be equivalent to
=AVERAGE(J1:J8) and so on
I have found that the only way to solve the problem of formulas changing in Excel Tables when you insert in VBA is to insert at the first row of the table, NOT the bottom or the middle. You can sort after.
And I always select or reference the EntireRow to do my insert in the Worksheet object not in the table itself. I always put a table in its own Worksheet anyway using xx.Entirerow.Insert.

Using the OpenXmlSDK, how can I select/update the value in a cell that is a range-control?

I'm not even sure if I'm using the correct terminology; I will update the question and title as required.
I'm using the OpenXmlSDK to populate a pre-existing Excel 2010 .xlsm file - a macro-enabled worksheet.
I can access worksheets and cells fairly well.
However, I can't figure out how to either access or update the data in a cell that is a dropdown-control that takes its values from a range on another worksheet.
It's labelled as "H13" on the worksheet, and right-click >> format control shows
Input range: 'anotherWorksheet'!$N$3:$N$54
Cell link: 'anotherWorksheet'!$M$3
Whenever I try to get a reference to this cell, I can't find it -- I get a null value
I've tried two access methods:
I'm not even sure if I'm using the correct terminology; I will update the question and title as required.
I'm using the OpenXmlSDK to populate a pre-existing Excel 2010 .xlsm file - a macro-enabled worksheet.
I can access worksheets and cells fairly well.
However, I can't figure out how to either access or update the data in a cell that is a dropdown-control that takes its values from a range on another worksheet.
It's labelled as "H13" on the worksheet, and right-click >> format control shows
Input range: 'anotherWorksheet'!$N$3:$N$54
Cell link: 'anotherWorksheet'!$M$3
Whenever I try to get a reference to this cell, I can't find it -- I get a null value
I've tried two access methods:
// http://msdn.microsoft.com/en-us/library/ff921204.aspx
private static Cell GetCell(Worksheet worksheet, string addressName)
{
return worksheet.Descendants<Cell>().Where(
c => c.CellReference == addressName).FirstOrDefault();
}
and:
// Given a worksheet, a column name, and a row index,
// gets the cell at the specified column and
// http://stackoverflow.com/questions/527028/open-xml-sdk-2-0-how-to-update-a-cell-in-a-spreadsheet
private static Cell GetCell(Worksheet worksheet, string columnName, uint rowIndex)
{
Row row = GetRow(worksheet, rowIndex);
if (row == null)
return null;
return row.Elements<Cell>().Where(c => string.Compare
(c.CellReference.Value, columnName +
rowIndex, true) == 0).FirstOrDefault();
}
// Given a worksheet and a row index, return the row.
private static Row GetRow(Worksheet worksheet, uint rowIndex)
{
return worksheet.GetFirstChild<SheetData>().
Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
}
Both yield null for the target cell H13, but provide references to surrounding cells (ie, `H12, H14, G13'
Actually, I13 also yields null, but that cell is not populated with anything. However, if I can't get a reference, how could I populate it with the SDK? Not my main point, here.
I will be receiving data that will match one of the entries in the dropdown; I just need to actually populate/select that particular entry in the target spreadsheet.
How can I do this with the OpenXmlSDK? I've tried using various open-source libraries, but none seem to support .xslm files (file provided by the client, and cannot be used in another format; macros must execute on launch, etc.).
Although I'm using C#, since my question is about the OpenXmlSDK, I would accept answers in other languages using that framework.
Short Answer: The cell does not exist, thus the null reference.
I created a small worksheet with a list (dropdown) DataValidation pointing to a range of cells in another worksheet.
Reflecting the file using the Open XML SDK 2.0 Productivity Tool I saw that instead of a cell being created and appended to the worksheet, a DataValidation (with a CellReference equating to the target) was created, instead.
using X14 = DocumentFormat.OpenXml.Office2010.Excel;
[....]
X14.DataValidations dataValidations1 = new X14.DataValidations() { Count = (UInt32Value)1U };
dataValidations1.AddNamespaceDeclaration("xm", "http://schemas.microsoft.com/office/excel/2006/main");
X14.DataValidation dataValidation1 = new X14.DataValidation() { Type = DataValidationValues.List, AllowBlank = true, ShowInputMessage = true, ShowErrorMessage = true };
X14.DataValidationForumla1 dataValidationForumla11 = new X14.DataValidationForumla1();
Excel.Formula formula1 = new Excel.Formula();
formula1.Text = "Lists!$A$1:$A$51";
dataValidationForumla11.Append(formula1);
Excel.ReferenceSequence referenceSequence1 = new Excel.ReferenceSequence();
referenceSequence1.Text = "A1";
dataValidation1.Append(dataValidationForumla11);
dataValidation1.Append(referenceSequence1);
dataValidations1.Append(dataValidation1);
Unless the worksheet location has a preset value, it will not actually "be" a "cell" when accessed at run-time.
In retrospect, it makes sense. But visually, it looks like a cell, so it's not obvious....
NOT A CELL:
NOTE: if a selection is made from the DataValidation, and saved, the cell now has a value, and so it exists:
IS A CELL:
This can be worked-around by creating and appending a new cell to the target row when a null reference is returned.
The difficulty now lies in the validation requiring a reference to the shared strings table, and will not accept raw-text as the cell value....
UPDATE: I was able to find the DataValidation associated with the cell, find the target worksheet and range, find the target value within that range, and get the SharedStringsTable reference associated with that value. But no matter value I plugged into the target cell, it was all considered bad data by Excel upon opening.
In the end, I gave up, went slinking back to Excel Interop, and was find a method to select a dropdown field from within (ugh) Interop.

Resources