I created an XSSFTable with below example code:
https://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/CreateTable.java
One column in my XSSFTable is a formula that referencing to another column in this table.
For example, in XSSFTable TBL column ColumnA, the formula is: =[#[ColumnB]], I can set the formula on each cell in ColumnA via cell.setCellFormula("TBL[[#This Row],[ColumnB]]"), but it will have problem while opened in Excel and Excel has to remove the formula in order to display the worksheet correctly.
This problem only happened in creating blank new XSSFWorkbook, if it is loaded from an existing .xlsx file created by Excel, it is able to modify the formula via cell.setCellFormula() and able to open in Excel correctly.
If there are any sample code can work correctly in this situation?
Main problem with the linked example is that it names all columns equal "Column":
...
for(int i=0; i<3; i++) {
//Create column
column = columns.addNewTableColumn();
column.setName("Column");
column.setId(i+1);
...
So formula parser cannot difference between them.
But the whole logic of filling the table column headers and filling the sheet contents using one loop is not really comprehensible. So here is a more appropriate example:
public class CreateTable {
public static void main(String[] args) throws IOException {
Workbook wb = new XSSFWorkbook();
XSSFSheet sheet = (XSSFSheet) wb.createSheet();
//Create
XSSFTable table = sheet.createTable();
table.setDisplayName("Test");
CTTable cttable = table.getCTTable();
//Style configurations
CTTableStyleInfo style = cttable.addNewTableStyleInfo();
style.setName("TableStyleMedium2");
style.setShowColumnStripes(false);
style.setShowRowStripes(true);
//Set which area the table should be placed in
AreaReference reference = new AreaReference(new CellReference(0, 0),
new CellReference(4,2));
cttable.setRef(reference.formatAsString());
cttable.setId(1);
cttable.setName("Test");
cttable.setTotalsRowCount(1);
CTTableColumns columns = cttable.addNewTableColumns();
columns.setCount(3);
CTTableColumn column;
XSSFRow row;
XSSFCell cell;
//Create 3 columns in table
for(int i=0; i<3; i++) {
column = columns.addNewTableColumn();
column.setName("Column"+i);
column.setId(i+1);
}
//Create sheet contents
for(int i=0; i<5; i++) {//Create 5 rows
row = sheet.createRow(i);
for(int j=0; j<3; j++) {//Create 3 cells each row
cell = row.createCell(j);
if(i == 0) { //first row is for column headers
cell.setCellValue("Column"+j);
} else if(i<4){ //next rows except last row are data rows, last row is totals row so don't put something in
if (j<2) cell.setCellValue((i+1)*(j+1)); //two data columns
else cell.setCellFormula("Test[[#This Row],[Column0]]*Test[[#This Row],[Column1]]"); //one formula column
}
}
}
FileOutputStream fileOut = new FileOutputStream("ooxml-table.xlsx");
wb.write(fileOut);
fileOut.close();
wb.close();
}
}
Related
I am trying to generate an Excel workbook which will be a template.
For now I am trying to generate a workbook with 1 sheet that holds only header cells with values, with certain Height and Width values. The problem is not that I cannot do it, but when I generate/create the .xlsx file the cells are hidden in a certain way and I have to click N(if there are 13 cells) times to display them all.
[Example of how Cells are hidden]
[1]: https://i.stack.imgur.com/xYh1P.png
And the way I want them to be displayed upon creation of the file is like this.
[Example of how I wish them to be displayed uppon creation]
[2]: https://i.stack.imgur.com/FWILw.png
The code is as follows
//creating workbook
private static void createWorkBookFile() throws IOException {
String filePath = "C:\\UltimateMapper\\UltimateMapperProject\\";
filePath+="\\WriteTestFiles";
System.out.print("Enter name of file: ");
String fileName = scan.nextLine();
filePath+="\\"+fileName+".xlsx";
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet currentSheet = workbook.createSheet("File to STG");
XSSFRow row;
XSSFCell cell;
//TO DO START FORM HERE FINISH AND THEN INSIDE LOOP
String[] fileToSTGCellValues_Names = new String[] {"Source Location_Schema","Source File_Table Name",
"Source_Field_Column Name","Start Pos","End Pos",
"Source Field Length","Source Field_Column Data Type",
"Transformation","Target Location_Schema","Target File_Table Name",
"Target Field_Column Name","Target Field_Column Data Type","Comments"};
HashMap<String,Integer> fileToSTGCellValues_Widths = new HashMap<String,Integer>();
fileToSTGCellValues_Widths.put("Source Location_Schema",26);
fileToSTGCellValues_Widths.put("Source File_Table Name",26);
fileToSTGCellValues_Widths.put("Source_Field_Column Name",35);
fileToSTGCellValues_Widths.put("Start Pos",10);
fileToSTGCellValues_Widths.put("End Pos",10);
fileToSTGCellValues_Widths.put("Source Field Length",18);
fileToSTGCellValues_Widths.put("Source Field_Column Data Type",21);
fileToSTGCellValues_Widths.put("Transformation",43);
fileToSTGCellValues_Widths.put("Target Location_Schema",18);
fileToSTGCellValues_Widths.put("Target File_Table Name",36);
fileToSTGCellValues_Widths.put("Target Field_Column Name",36);
fileToSTGCellValues_Widths.put("Target Field_Column Data Type",20);
fileToSTGCellValues_Widths.put("Comments",47);
int headeRowNumber = 0;
row = currentSheet.createRow(headeRowNumber);
row.setHeightInPoints(28.50f);
for(int i =0;i<13;i++) {
currentSheet.setColumnWidth(i, fileToSTGCellValues_Widths.get(fileToSTGCellValues_Names[i]));
cell = row.createCell(i);
cell.setCellValue(fileToSTGCellValues_Names[i]);
}
FileOutputStream fout = new FileOutputStream(filePath);
workbook.write(fout);
fout.close();
System.out.println("File created");
}```
In my case I need to protect single cell, to achieve this initially I used HSSFCellStyle and setLocked(true) and protect the sheet using Sheet.protectSheet("password"). This will protect non empty cell also so I am using DataValidation with single option, It is working as expected but it allows to delete the cell content without validation.Below is my sample code.Thanks in advance for your help.
String errorBoxTitle = "Warning";
String errorBoxMessage = "Invalid Data";
String [] valueArr = {"cellValue"};
CellRangeAddressList cellValueAddress = new CellRangeAddressList(row.getRowNum(), row.getRowNum(), cell.getColumnIndex(), cell.getColumnIndex());
DVConstraint cellValueConstraint = DVConstraint.createExplicitListConstraint(valueArr);
DataValidation cellValueValidation = new HSSFDataValidation(cellValueAddress , cellValueConstraint );
cellValueValidation .setSuppressDropDownArrow(true);
cellValueValidation .createErrorBox(errorBoxTitle, errorBoxMessage);
cellValueValidation .setEmptyCellAllowed(false);
sheet.addValidationData(cellValueValidation );
A cell either can be locked or not locked. If it is locked, then it cannot be changed and also not be deleted. If a cell is not locked, then of course it also can be deleted. So since data validation needs to be used in not locked cells, data validation is not an option to protect against deleting.
If the goal is to have only some cells locked when the sheet is protected but most of the cells shall be not locked, then the only way is creating a cell style having setLocked(false) set and applying that cell style to all cells which shall be not locked. That is because it is the default in Excel that cells are locked when the sheet is protected.
If new cells in whole columns shall be not locked, then this notLocked cell style can be set as the default column style.
In the following example only the header cells A1:C1 and all cells in columns greater than C are locked. The cells in A2:C4 are not locked because the notLocked cell style is applied to that cells. Also the empty cells in columns A:C for rows greater than 4 are not locked because the notLocked cell style is applied as the default column style for columns A:C.
import java.io.FileOutputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
public class CreateExcelDefaultColumnStyleNotLocked {
public static void main(String[] args) throws Exception {
//Workbook workbook = new XSSFWorkbook(); String filePath = "./CreateExcelDefaultColumnStyleNotLocked.xlsx";
Workbook workbook = new HSSFWorkbook(); String filePath = "./CreateExcelDefaultColumnStyleNotLocked.xls";
CellStyle notLocked = workbook.createCellStyle();
notLocked.setLocked(false);
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);
Cell cell = null;
for (int c = 0; c < 3; c++) {
cell = row.createCell(c);
cell.setCellValue("Col " + (c+1));
}
for (int r = 1; r < 4; r++) {
row = sheet.createRow(r);
for (int c = 0; c < 3; c++) {
cell = row.createCell(c);
cell.setCellValue(r * (c+1));
cell.setCellStyle(notLocked);
}
}
sheet.setDefaultColumnStyle(0, notLocked);
sheet.setDefaultColumnStyle(1, notLocked);
sheet.setDefaultColumnStyle(2, notLocked);
sheet.protectSheet("");
FileOutputStream out = new FileOutputStream(filePath);
workbook.write(out);
out.close();
workbook.close();
}
}
I want to click on an exact row in a WinTable where my criteria meets but couldnt succeed so far. I can search criteria for an exact row but I can not get total number of rows so that I would make a loop for all rows. I tried collection and table.Rows.Count,but both brings nothing to me. Can someone help me on this ?
#region Variable Declarations
WinTable uIG1Table = this.UIProMANAGEWindow.UIDefinitionsWindow.UIG1Window.UIG1Table;
WinRow dataGridrow = uIG1Table.GetRow(0);
#endregion
UITestControlCollection rows = uIG1Table.Rows;
// MessageBox.Show(rows[5].RowIndex.ToString());
foreach (WinRow row in uIG1Table.Rows)
{
foreach (WinCell cell in row.Cells)
{
if (cell.Value.ToString() == "E81")
Mouse.Click(cell, new Point(5, 0));
}
}
and this is the code with for loop
int rows = uIG1Table.Rows.Count;
for (int i = 0; i < rows; i++)
{
foreach (WinCell cell in dataGridrow.Cells)
{
if (cell.Value.ToString() == "E81")
Mouse.Click(cell, new Point(5, 0));
}
}
When doing a GetChildren() on a row, you will notice that the first child is of type RowHeader. A user typically clicks the row header to select the row.
Following code will iterate all the rows in a DataGridView and click the row header, effectively selecting the row:
UITestControlCollection rows = YourUIMapTable.Rows;
foreach (UITestControl row in rows)
{
UITestControl rowHeader = row.GetChildren().Single(child => child.ControlType == ControlType.RowHeader);
Mouse.Click(rowHeader);
}
If you want to select a specific row, you can do something like this:
Mouse.Click(YourUIMapTable.Rows[putIndexNumberHere].GetChildren().Single(child => child.ControlType == ControlType.RowHeader));
The sample code above is based on the program I wrote in my answer to this question:
How to get cell color information of a WinCell with codedui?
I have some Data to put them into an Excel sheet, 4 values, n rows. I tried this:
final HSSFWorkbook wb = new HSSFWorkbook();
final HSSFSheet sheet = wb.createSheet();
FileOutputStream stream;
HSSFRow row = null;
try {
stream = new FileOutputStream(targetDirectory + "workbook.xls");
for (int i = 1; i < data.size(); i++) {
row = sheet.createRow((short) i);
cell = row.createCell(0);
cell.setCellValue("Das");
cell = row.createCell(1);
cell.setCellValue("hier");
cell = row.createCell(2);
cell.setCellValue("soll");
cell = row.createCell(3);
cell.setCellValue("rein");
wb.write(stream);
}
stream.close();
} catch ...
While writing into the EXCEL file I can watch growing the size to 1.600 kB. When I open the sheet I see one row. Closing the file without saving decrease the size to 4 kB.
It seems as the rows where all there but invisible?
Any idea, what am I doing wrong?
I'm not sure why, but you seem to be writing the workbook out on every single row, always to the same file. That means you'll end up with multiple copies of the workbook in the same file, which isn't really what you wanted at all!
Move the wb.write call to the end, and it should all work just fine. Excel is a binary format, it's not like CSV, so you should only write the file out when you're finished
I am using openxml to create an excel report. The openxml operates on a template excel file using named ranges.
The client requires a totals row at the end of the list of rows. Sounds like a reasonable request!!
However, the data table I'm returning from the db can contain any number of rows. Using template rows and 'InsertBeforeSelf', my totals row is getting overridden.
My question is, using openxml, how can I insert rows into the spreadsheet, causing the totals row to be be moved down each time a row is inserted?
Regards ...
Assuming you're using the SDK 2.0, I did something similiar by using this function:
private static Row CreateRow(Row refRow, SheetData sheetData)
{
uint rowIndex = refRow.RowIndex.Value;
uint newRowIndex;
var newRow = (Row)refRow.Clone();
/*IEnumerable<Row> rows = sheetData.Descendants<Row>().Where(r => r.RowIndex.Value >= rowIndex);
foreach (Row row in rows)
{
newRowIndex = System.Convert.ToUInt32(row.RowIndex.Value + 1);
foreach (Cell cell in row.Elements<Cell>())
{
string cellReference = cell.CellReference.Value;
cell.CellReference = new StringValue(cellReference.Replace(row.RowIndex.Value.ToString(), newRowIndex.ToString()));
}
row.RowIndex = new UInt32Value(newRowIndex);
}*/
sheetData.InsertBefore(newRow, refRow);
return newRow;
}
I'm not sure how you were doing it with InsertBeforeSelf before, so maybe this isn't much of an improvement, but this has worked for me. I was thinking you could just use your totals row as the reference row. (The commented out part is for if you had rows after your reference row that you wanted to maintain. I made some modifications, but it mostly comes from this thread: http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/65c9ca1c-25d4-482d-8eb3-91a3512bb0ac)
Since it returns the new row, you can use that object then to edit the cell values with the data from the database. I hope this is at least somewhat helpful to anyone trying to do this...
[Can someone with more points please put this text as a comment for the M_R_H's Answer.]
The solution that M_R_H gave helped me, but introduces a new bug to the problem. If you use the given CreateRow method as-is, if any of the rows being moved/re-referenced have formulas the CalcChain.xml (in the package) will be broken.
I added the following code to the proposed CreateRow solution. It still doesn't fix the problem, because, I think this code is only fixing the currently-being-copied row reference:
if (cell.CellFormula != null) {
string cellFormula = cell.CellFormula.Text;
cell.CellFormula = new CellFormula(cellFormula.Replace(row.RowIndex.Value.ToString(), newRowIndex.ToString()));
}
What is the proper way to fix/update CalcChain.xml?
PS: SheetData can be gotten from your worksheet as:
worksheet.GetFirstChild<SheetData>();
You have to loop all rows and cells under the inserted row,change its rowindex and cellreference. I guess OpenXml not so smart that help you change index automatically.
static void InsertRow(string sheetName, WorkbookPart wbPart, uint rowIndex)
{
Sheet sheet = wbPart.Workbook.Descendants<Sheet>().Where((s) => s.Name == sheetName).FirstOrDefault();
if (sheet != null)
{
Worksheet ws = ((WorksheetPart)(wbPart.GetPartById(sheet.Id))).Worksheet;
SheetData sheetData = ws.WorksheetPart.Worksheet.GetFirstChild<SheetData>();
Row refRow = GetRow(sheetData, rowIndex);
++rowIndex;
Cell cell1 = new Cell() { CellReference = "A" + rowIndex };
CellValue cellValue1 = new CellValue();
cellValue1.Text = "";
cell1.Append(cellValue1);
Row newRow = new Row()
{
RowIndex = rowIndex
};
newRow.Append(cell1);
for (int i = (int)rowIndex; i <= sheetData.Elements<Row>().Count(); i++)
{
var row = sheetData.Elements<Row>().Where(r => r.RowIndex.Value == i).FirstOrDefault();
row.RowIndex++;
foreach (Cell c in row.Elements<Cell>())
{
string refer = c.CellReference.Value;
int num = Convert.ToInt32(Regex.Replace(refer, #"[^\d]*", ""));
num++;
string letters = Regex.Replace(refer, #"[^A-Z]*", "");
c.CellReference.Value = letters + num;
}
}
sheetData.InsertAfter(newRow, refRow);
//ws.Save();
}
}
static Row GetRow(SheetData wsData, UInt32 rowIndex)
{
var row = wsData.Elements<Row>().
Where(r => r.RowIndex.Value == rowIndex).FirstOrDefault();
if (row == null)
{
row = new Row();
row.RowIndex = rowIndex;
wsData.Append(row);
}
return row;
}
Above solution got from:How to insert the row in exisiting template in open xml. There is a clear explanation might help you a lot.