Autowidth is not working for merged columns - apache-poi

{
row=sheet.createRow(0);
cell=row.createCell(0);
cell.setCellValue("header");
cell=row.createCell(1);
sheet.addMergedRegion(new CellRangeAddress(0,0,0,1));
row=sheet.createRow(1);
cell=row.createCell(0);
cell.setCellValue("Keys");
cell=row.createCell(1);
cell=row.setCellValue("Values");
row=sheet.createRow(2);
cell=row.createCell(0);
cell.setCellValue("No data");
cell=row.createCell(1);
sheet.addMergedRegion(new CellRangeAddress(1,1,0,1);
sheet.autoSizeColumn(0);
}
autosize is working when I merged two columns of row zero, but after merging two columns of second row autosize is not working..
thanks in advance...

I've spotted your problem, it's this line:
sheet.autoSizeColumn(0);
From the javadocs on autoSizeColumn(int) we see this key bit of information:
Default is to ignore merged cells.
You need to switch to instead call autoSizeColumn(int,boolean), and pass in a true value. This will tell POI to take account of merged cells during the sizing. So, your code should instead be:
sheet.autoSizeColumn(0, true);

Related

Autosize only Merged Cells using Aspose

I saw that there was a way to autofit rows in the worksheet. But I only want to autofit the rows that have only merged cells in it. And keep all of the other rows the same. Not sure if there is a way to do this.
I've tried this but it autofits all rows.
AutoFitterOptions options = new AutoFitterOptions();
options.AutoFitMergedCells = true;
_worksheet.AutoFitRows(options);
And I won't know the exact row that needs to be autofitted because I'm adding data to the excel sheet.
Please try the following sample code for your needs:
e.g.
Sample code:
Workbook wb = new Workbook("e:\\test2\\Book1.xlsx");
Worksheet _worksheet = wb.Worksheets[0];
var cells = _worksheet.Cells;
foreach (Cell cell in cells)
{
if (cell.IsMerged)
{
int row = cell.Row;
AutoFitterOptions options = new AutoFitterOptions();
options.AutoFitMergedCells = true;
_worksheet.AutoFitRows(row, row, options);
}
}
wb.Save("e:\\test2\\out1.xlsx");
Hope, this helps a bit.
You may also post your queries in dedicated forums.
PS. I am working as Support developer/ Evangelist at Aspose.

How to keep table formatting when sorting table generated by PHPSpreadsheet?

I have generated an Excel table using PHPSpreadsheet including the style and the autofilter:
The problem is when I sort the data by the second and third columns, the table formatting is gone. This is how it looks like compared if I use Table Style directly from Excel (using Home-> Format as Table):
Is there any way to keep the formatting when I sort the table generated from PHPSpreadsheet?
Relevant PHP Code:
for ($rowNumber = 0, $rowNumberMax = sizeof($rows); $rowNumber < $rowNumberMax; $rowNumber++) //rows (all data)
{
$columnNumber = 0; //1 = A
for ($i = 0, $j = sizeof($tableColumns); $i < $j; $i++) //loop through table header label
{
foreach ($rows[$rowNumber] as $rowKey => $rowValue) //loop through single row data
{
if($tableColumns[$i] == $rowKey)
{
$sheet->setCellValueByColumnAndRow($columnNumber + 1, ($rowNumber + 5), $rowValue);
$currentCell = Utilities::num2alpha($columnNumber) .''. ($rowNumber + 5);
$sheet->getStyle($currentCell)->getNumberFormat()->setFormatCode('#');
$sheet->getStyle($currentCell)->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
if(($rowNumber+5) % 2 == 0)
{
//even row
$sheet->getStyle($currentCell)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('ffd9e1f2');
}
else
{
//odd row
}
$columnNumber++;
break;
}
}
}
}
//set autofilter
$headerFirstCellPosition = 'A4';
$tableLastCellPosition = Utilities::num2alpha(sizeof($tableColumns) - 1) . '' . (sizeof($rows) + 4);
$sheet->setAutoFilter($headerFirstCellPosition . ':' . $tableLastCellPosition);
The problem is you were just applying formatting to the cells based on if the row was even or odd, but it wasn't actually replicating a table in Excel. You would find the same result in Excel if you just formatted every other row like you did with your PHP code, where the "table" format would get lost.
Somebody just recently implemented a first pass of the actual table feature in Excel: https://github.com/PHPOffice/PhpSpreadsheet/pull/2671
You need to be on PHPSpreadSheet version 1.23.0 in order to be able to use this.
Using that, you would have to modify your code but you can go to the Samples section in the code area and view how to implement it: https://github.com/PHPOffice/PhpSpreadsheet/tree/master/samples/Table
https://github.com/PHPOffice/PhpSpreadsheet/blob/master/samples/Table/01_Table.php
Here is the relevant code (I removed some of the lines and added additional comments from the 01_Table.php sample at the link provided).
Table styles can be found here: https://github.com/PHPOffice/PhpSpreadsheet/blob/master/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php
// Create Table
$table = new Table('A1:D17', 'Sales_Data');
// Create Table Style
$tableStyle = new TableStyle();
// this line is the style type you want, you can verify this in Excel by clicking the "Format as Table" button and then hovering over the style you like to get the name
$tableStyle->setTheme(TableStyle::TABLE_STYLE_MEDIUM2);
// this gives you the alternate row color; I suggest to use either this or columnStripes as both together do not look good
$tableStyle->setShowRowStripes(true);
// similar to the alternate row color but does it for columns; I suggest to use either this or rowStripes as both together do not look good; I personally set to false and only used the rowStripes
$tableStyle->setShowColumnStripes(true);
// this will bold everything in the first column; I personally set to false
$tableStyle->setShowFirstColumn(true);
// this will bold everything in the last column; I personally set to false
$tableStyle->setShowLastColumn(true);
$table->setStyle($tableStyle);
Also make sure that you include the following to be able to use these:
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle;
Implementing that into your code will then allow you to sort using the auto filters and keep the formatting like you are expecting.
There are a few caveats such as:
Note that PreCalculateFormulas needs to be disabled when saving spreadsheets containing tables with formulae (totals or column formulae).
Also, as I am actually currently working on doing this, it doesn't look like you can apply an autofilter and have a table at the same time at this point.
That does appear to be on the todo list though, as the first link I provided the contributor has "Filter expressions similar to AutoFilter."
Otherwise, that should get you what you want and aside from being able to auto filter prior to creating the Excel file, it has worked well in my small testing.
Edit to add:
I think you can actually simplify your code a bit by using the functionality of PHPSpreadsheet to create a a spreadsheet from an array.
Documentation from PHPSpreadsheet can be found here: https://phpspreadsheet.readthedocs.io/en/latest/topics/accessing-cells/#setting-a-range-of-cells-from-an-array
You'll need to change it so that the array that is holding the info starts with your headers, so I believe that would look similar to this for your code:
$rows = [
['header1', 'header2', 'header3', 'header4']
];
Then you can populate the $rows array with your data from the rows either with a loop or just a single declaration depending on what you are putting in there, but basically using the below to populate the array.
$rows[] = [
$field1Data,
$field2Data,
$field3Data,
$field4Data
];
After you do that, you can then generate the spreadsheet using the following:
$sheet->getActiveSheet()
->fromArray(
$rows, // the data to set
NULL, // array values with this value will not be set
'A1', // top left coordinate of the worksheet range where we want to set these values (default is A1)
true // adds 0 to cell instead of blank if a 0 is the value
);
After doing the above, you can then add the code to create the table I posted and then save the file and you should be good.
Also, if you are in a situation where you still need to use the autofilter (for instance if you want to pre-filter the file on one or more columns which at this point you can't use a table when doing), you can make the autofilter call a bit easier.
// determine the the number of rows in the active sheet
$highestRow = $spreadsheet->getActiveSheet()->getHighestRow();
// get the highest column letter
$highestColumn = $spreadsheet->getActiveSheet()->getHighestColumn();
// set autofilter range
$spreadsheet->getActiveSheet()->setAutoFilter('A1:'.$highestColumn.$highestRow);
I realize the additional edit goes beyond the question, but figured I'd point it out since there are some built-in methods that you could use to reduce some of your code.
-Matt

Remove blank row while pasting data from Excel to Angular Kendo UI grid

I am using the following example:
excel-paste
Please look at the excel-paste.directive.ts
Once the data is pasted, I need to validate and save data to database. When I paste data from Excel to the grid, in the directive it uses line feed to split and adds an extra blank row at the bottom. I tried replacing line feed with carriage return using '\r\n', but that does the same.
const rows = data.split('\n');
Please guide if there is a way to remove that empty line
You can use the Array.prototype.pop() function, to remove the last element of the array.
const rows = data.split('\n');
rows.pop();
Alternatively, if you wanted to do it all in one line you could use the Array.prototype.slice() function to copy all elements except for the last from the array.
const rows = data.split('\n').slice(0, -1);

How can we include the cell formula while export to excel from .rdlc

In my rdlc report have following columns
SlNo, Item, Uom, Qty, Rate, Amount
Here the Amount field is a formula (Rate*Qty)
The report is working fine, and when i export to excel also displaying the values are correctly.
But my problem is, after export to excel, when i change the Qty or Rate columns in excel file the Amount is not get changed automatically, because the formula is missing in the excel cell.
How can we include the formula in Amount column while export to excel from .rdlc?
I'm afraid that this required behaviour isn't really possible by just using the rdlc rendering.
In my search I stumbled upon this same link that QHarr posted: https://social.msdn.microsoft.com/Forums/en-US/3ddf11bf-e10f-4a3e-bd6a-d666eacb5ce4/report-viewer-export-ms-report-data-to-excel-with-formula?forum=vsreportcontrols
I haven't tried the project that they're suggesting but this might possibly be your best solution if it works. Unfortunately I do not have the time to test it myself, so if you test this please share your results.
I thought of the following workaround that seems to work most of the times, but isn't really that reliable because the formula sometimes gets displayed as full-text instead of being calculated. But I guess this could be solved by editing the excel file just after being exported, and changing the cell properties of this column containing the formula or just triggering the calculate.
Using the built-in-field Globals!RenderFormat.Name you can determine the render mode, this way you can display the result correctly when the report is being rendered to something different than Excel. When you export to Excel, you could change the value of the cell to the actual formula.
To form the formula it's self you'll need to figure this out on your own, but the RowNumber(Scope as String) function can be of use here to determine the row number of your cells.
Here is a possible example for the expression value of your amount column
=IIF(Globals!RenderFormat.Name LIKE "EXCEL*", "=E" & Cstr(RowNumber("DataSet1")+2) & "*F" & Cstr(RowNumber("DataSet1")+2) ,Fields!Rate.Value * Fields!Qty.Value )
Now considering that this formula sometimes gets displayed as full-text, and you'll probably have to edit the file post-rendering. If it's too complicated to determine which row/column the cell is on, you could also do this post-rendering. But I believe that the above expression should be easy enough to use to get your desired result without having to do much after rendering.
Update: The following code could be used to force the calculation of the formula (post rendering)
var fpath = #"C:\MyReport.xlsx";
using (var fs = File.Create(fpath))
{
var lr = new LocalReport();
//Initializing your reporter
lr.ReportEmbeddedResource = "MyReport.rdlc";
//Rendering to excel
var fbytes = lr.Render("Excel");
fs.Write(fbytes, 0, fbytes.Length);
}
var xlApp = new Microsoft.Office.Interop.Excel.Application() { Visible = false };
var wb = xlApp.Workbooks.Open(fpath);
var ws = wb.Worksheets[1];
var range = ws.UsedRange;
foreach (var cell in range.Cells)
{
var cellv = cell.Text as string;
if (!string.IsNullOrWhiteSpace(cellv) && cellv.StartsWith("="))
{
cell.Formula = cellv;
}
}
wb.Save();
wb.Close(0);
xlApp.Quit();

Using Range.TextToColumns to convert to multiple columns does not work

My goal is to convert a single csv string to multiple columns in excel using Range.TextToColumns().
I have a csv file whose columns can contain commas inside them, in that case the column value has double qoutes around it. So to avoid that issue I am just spliting at end of lines to get individual rows.
My plan is to put each csv row in a single cell in each excel row and use Text to columns function to get my column values staright a way without worrying about the commas inside column values.
Current code:
string row = lines[i - 1]; //single CSV row
var startCell = (Range)worksheet.Cells[i, 1];
var endCell = (Range)worksheet.Cells[i, 1];
var writeRange = worksheet.get_Range(startCell, endCell);
writeRange.Value2 = row;
startCell.TextToColumns(startCell, XlTextParsingType.xlDelimited
, XlTextQualifier.xlTextQualifierDoubleQuote, false
, false, false, true, false, false, false
, XlColumnDataType.xlGeneralFormat, false, false, false);
What happens after executing this code is that, I get all the row data in a single cell on each row instead of having multiple columns.
I have written the code in parallel to the manual approach we use while doing Text to Columns in excel sheet, the manual approach yielded expected results but the same thing when done over here in c# is failing.
Please point out mistakes if any in my approach, or show some way out.
Thanks in advance.
Ok, found the solution. Turns out the msdn guys have messed up the docs on this page:
Range.TextToColumns Documentation #msdn
startCell.TextToColumns(startCell, Excel.XlTextParsingType.xlDelimited,
Excel.XlTextQualifier.xlTextQualifierDoubleQuote, missing,
missing, missing, true, missing, missing, missing, missing,
missing, missing, missing);
So, instead of writting false, I was supposed to write missing for it to work properly.
This page helped me figure this out

Resources