i am having difficulties with open xml sdk:
i am trying to generate excel file with several columns that have numbers and i want to have total sum at the end
i have tried to Generate Table Definition Part Content and inside define every column (id, name etC). If column has true for TotalColumn, it adds code (rough example)
var column = new TableColumn{
id = 1,
name = "example",
TotalsRowFunction = TotalsRowFunctionValues.Sum,
TotalsRowFormula = new TotalsRowFormula("=SUBTOTAL(109;[" + rowName + "])")
};
I can't get it to work, when i open excel it reports error, but it doesn't explicitly says what the problem is... I tried with microsoft validator but can't figure anything out...
I'd appreciate any help / example code since i can't google anything out
EDIT:
i use this at the end:
workbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
workbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
Why not use a cell formula?
E.g.
cell.DataType = new EnumValue<CellValues>(CellValues.Number);
cell.CellFormula = new CellFormula(string.Format("=SUBTOTAL({0};[{1}])", "109", rowName));
//This will force a full recalculation of all the cells
workbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
workbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
I ended using EPPlus for this as it seems to be working simple and efficient
Related
I was trying to use this Microsoft tutorial Excel Chart Add-in - Javascript API
In the first example, it has the code
Excel.run(function (context) {
var sheet = context.workbook.worksheets.getItem("Sample");
var dataRange = sheet.getRange("A1:B13");
var chart = sheet.charts.add("Line", dataRange, "auto");
chart.title.text = "Sales Data";
chart.legend.position = "right"
chart.legend.format.fill.setSolidColor("white");
chart.dataLabels.format.font.size = 15;
chart.dataLabels.format.font.color = "black";
return context.sync();
}).catch(errorHandlerFunction);
If I run the code example I receive 2 errors. One that it cannot find excel from
Excel.run
And the errorhandler function is not defined, which appears to be correct.
Are these typos in new Microsoft documents? If not what have I got to change?
Version: excel 365 online build 16.0.13615.35052
2 things that you need to make sure you have in order to run this code succesfully.
Please add the errorHandlerFunction this could be be as easy as this:
function errorHandlerFunction(e ){
console.log("exception" + e );
}
Make sure you have a worksheet named "Sample". Make sure its exactly that name without trailing blank spaces.
I have a problem with my code. I generate an excel file unsing PhpSpreadsheet lib. So i can write my data and it worked fine. I add a data validation list in a cell using an other worksheet and it work too. So I try to add in a cell a formula which can change the value (using VLOOKUP) when someone select something in my data validation list and this work too.
But my problem is my formula is not calculated when I open my file. When I open the file I just see the formula and I wanted to see the result.
This is my code:
$i=2;
$j=6;
$filename = "export-excel.xlsx";
$spreadsheet = new Spreadsheet();
$dataSheet = $spreadsheet->getSheet(0);
$dataSheet1 = $spreadsheet->createSheet(1);
$dataSheet1->setTitle("Materiel");
$dataSheet1->setCellValue('A1','Modèle');
$dataSheet1->setCellValue('B1','Marque');
$dataSheet1->setCellValue('C1','Calibre');
$dataSheet1->setCellValue('D1','ITH');
$dataSheet1->setCellValue('E1','IMG');
$dataSheet1->setCellValue('F1','Type Matériel');
//remplissage matériel
$listMateriel = $this->entityManager->getRepository(Materiel::class)->findAll();
$nbrMateriel = count($listMateriel) +1;
foreach($listMateriel as $materiel){
$dataSheet1->setCellValue('A'.$i,$materiel->getModele());
$dataSheet1->setCellValue('B'.$i,$materiel->getMarque());
$dataSheet1->setCellValue('C'.$i,$materiel->getCalibre());
$dataSheet1->setCellValue('D'.$i,$materiel->getIth());
$dataSheet1->setCellValue('E'.$i,$materiel->getImg());
$dataSheet1->setCellValue('F'.$i,$materiel->getTypeMateriel()->getLibelle());
$i++;
}
//remplissage enveloppe
$enveloppe = $appareil->getEnveloppe();
$dataSheet->setCellValue('A5',1);
$dataSheet->setCellValue('B5',1);
$dataSheet->setCellValue('C5',$enveloppe->getLieu()->getLibelle());
$dataSheet->setCellValue('D5',$enveloppe->getNom());
$dataSheet->setCellValue('E5',$enveloppe->getNumeroschema());
$dataSheet->setCellValue('F5',$enveloppe->getCodegmao());
$dataSheet->setCellValue('G5',$enveloppe->getCodemire());
$dataSheet->setCellValue('H5',$enveloppe->getIk());
$dataSheet->setCellValue('I5',$enveloppe->getResponsable());
$dataSheet->setCellValue('J5',$enveloppe->getUnitegeo());
$dataSheet->setCellValue('K5',$enveloppe->getCentreMainteneur());
//remplissage appareil
foreach($enveloppe->getAppareils() as $app){
$dataSheet->setCellValue('A'.$j,1);
$dataSheet->setCellValue('B'.$j,2);
$dataSheet->setCellValue('C'.$j,$app->getRepere());
$dataSheet->setCellValue('D'.$j,$app->getNom());
$dataSheet->setCellValue('E'.$j,$app->getOrdreDansArmoire());
$dataSheet->setCellValue('F'.$j,$app->getMonotri());
$dataSheet->setCellValue('G'.$j,$app->getDepart());
$dataSheet->setCellValue('H'.$j,$app->getDdr());
$dataSheet->setCellValue('I'.$j,$app->getCalibreddr());
$dataSheet->setCellValue('J'.$j,$app->getMotorise());
$dataSheet->setCellValue('K'.$j,$app->getAlarme());
$dataSheet->setCellValue('L'.$j,(null === $app->getNaturealimentation()) ? '' : $app->getNaturealimentation()->getLibLong());
$dataSheet->setCellValue('M'.$j,$app->getSimultaneite());
$dataSheet->setCellValue('N'.$j,$app->getCompteur());
$dataSheet->setCellValue('O'.$j,(null === $app->getTypeconsommateur()) ? '' : $app->getTypeconsommateur()->getLibelle());
$dataSheet->setCellValue('P'.$j,$app->getTypecable());
$dataSheet->setCellValue('Q'.$j,$app->getNumcable());
$dataSheet->setCellValue('R'.$j,$app->getLongueurcable());
$dataSheet->setCellValue('S'.$j,$app->getSectioncable());
$dataSheet->setCellValue('T'.$j,$app->getMateriel()->getModele());
$this->dataValidation('T'.$j, $spreadsheet, $nbrMateriel);
$dataSheet->setCellValue('U'.$j,$app->getReglageIth());
$dataSheet->setCellValue('V'.$j,$app->getReglageImag());
$dataSheet->setCellValue('W'.$j,$app->getObservation());
//This is my formula who work fine
$dataSheet->setCellValueExplicit('X'.$j,'=SI(T'.$j.'<>"";RECHERCHEV(T'.$j.';Materiel!$A$2:$F$'.$nbrMateriel.';4;FAUX);"SO")', \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
$spreadsheet->getActiveSheet()->getCell('X'.$j)->getCalculatedValue();
Calculation::getInstance($spreadsheet)->disableCalculationCache();
$dataSheet->setCellValue('Y'.$j,$app->getMateriel()->getImg());
$j++;
}
$tmpPath = $this->getTmpFile2();
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setPreCalculateFormulas(true);
$writer->save($tmpPath);
This is what I have when I open my file:
I want to keep my formula in the cell and just show the result when I open my file. So someone have an Idea to how I can calculate my formula before to open my file ? I try with the documentation but it doesn't work.
Than in advance
The problem is that you use \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING in
$dataSheet->setCellValueExplicit('X'.$j,'=SI(T'.$j.'<>"";RECHERCHEV(T'.$j.';
Materiel!$A$2:$F$'.$nbrMateriel.';4;FAUX);"SO")', \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_STRING);
you should use TYPE_FORMULA.
A other common problem for non calculate are string operants instead of number typ operants in the formula. If you have string typs as operant the calculation excel refuse executed by opening the formula.
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();
Since writing varying sized data ranges to a sheet seems to remove an Excel Table if the data range is larger than the existing Excel tables range, I want to create a Table in Excel every time I run the code. I'm currently having a fair bit of difficulty creating the tables. The code I have right now to try and create the ListObject:
eSheets = e.ActiveWorkbook.Sheets;
eSheet = eSheets.get('Item', j);
eSheet.Activate;
eSheet.Range(horzcat('A1:R',mat2str(size(obj,1)+1))).Select;
eSheet.Listobjects.Add;
eSheet.Listobjects.Item(1).TableStyle = 'TableStyleMedium2';
eSheet.ListObjects.Item(1).Name = tablename;
Any commentary or suggestions would be appreciated
I dont know about using eSheet in matlab but with the function
xlswrite(filename,A,sheet,xlRange)
you can also write your data from a matrix to an excel table http://de.mathworks.com/help/matlab/ref/xlswrite.html and with
[A,B] = xlsfinfo('foofoo.xlsx');
sheetValid = any(strcmp(B, 'foo2'));
you can also check if a table sheet already exist so that you wont override the old one and create a new one, as seen in https://de.mathworks.com/matlabcentral/answers/25848-how-to-check-existence-of-worksheet-in-excel-file
I am not sure if this is what you are looking for thougth
Alright, since the post got downvoted (not sure why...) I found my own answer with the help of some VBA forums and MATLAB Newsgroup. Here's what the final code looks like for anyone else that has issues:
e = actxserver('Excel.Application');
ewb = e.Workbooks.Open('Path/to/file');
eSheets = e.ActiveWorkbook.Sheets;
eSheet = eSheets.get('Item', j);
eSheet.Activate;
range = horzcat('A1:R',mat2str(size(obj,1)+1));
range_todelete = horzcat('A1:R',mat2str(size(obj,1)+300));
Range1 = eSheet.get('Range',range_todelete);
Range1.Value=[];
eSheet.Range(range).Select;
name = 'Table_Name';
try eSheet.ListObjects(name).Item(1).Delete
catch
end
eSheet.Listobjects.Add;
eSheet.ListObjects.Item(1).Name = name;
eSheet.ListObjects.Item(1).TableStyle = 'TableStyleMedium2';
Range = eSheet.get('Range',range);
Range.Value = cellarray;
Currently I have an application that takes information from a SQLite database and puts it to Excel. However, I'm having to take each DataRow, iterate through each item, and put each value into it's own cell and determine highlighting. What this is causing is 20 minutes to export a 9000 record file into Excel. I'm sure it can be done quicker than that. My thoughts are that I could use a data source to fill the Excel Range and then use the column headers and row numbers to format only those rows that need to be formatted. However, when I look online, no matter what I seem to type, it always shows examples of using Excel as a database, nothing about importing into excel. Unless I'm forgetting a key word or to. Now, this function has to be done in code as it's part of a bigger application. Otherwise I would just have Excel connect to the DB and pull the information itself. Unfortunately that's not the case. Any information that could assist me in quick loading an excel sheet would be appreciated. Thanks.Additional Information:Another reason why the pulling of the information from the DB has to be done in code is that not every computer this is loaded on will have Excel on it. The person using the application may simply be told to export the data and email it to their supervisor. The setup app includes the needed dlls for the application to make the proper format.Example Code (Current):
For Each strTemp In strColumns
excelRange = worksheet.Cells(1, nCounter)
excelRange.Select()
excelRange.Value2 = strTemp
excelRange.Interior.Color = System.Drawing.Color.Gray.ToArgb()
excelRange.BorderAround(Excel.XlLineStyle.xlContinuous, Excel.XlBorderWeight.xlThin, Excel.XlColorIndex.xlColorIndexAutomatic, Type.Missing)
nCounter += 1
Next
Now, this is only example code in terms of the iteration I'm doing. Where I'm really processing the information from the database I'm iterating through a dataTable's Rows, then iterating through the items in the dataRow and doing essentially the same as above; value by value, selecting the range and putting the value in the cell, formatting the cell if it's part of a report (not always gray), and moving onto the next set of data. What I'd like to do is put all of the data in the excel sheet (A2:??, not a row, but multiple rows) then iterate through the reports and format each row then. That way, the only time I iterate through all of the records is when every record is part of a report.
Ideal Code
excelRange = worksheet.Cells("A2", "P9000")
excelRange.DataSource = ds 'ds would be a queried dataSet, and I know there is no excelRange.DataSource.
'Iteration code to format cells
Update:
I know my examples were in VB, but it's because I was also trying to write a VB version of the application since my boss prefers VB. However, here's my final code using a Recordset. The ConvertToRecordset function was obtained from here.
private void CreatePartSheet(Excel.Worksheet excelWorksheet)
{
_dataFactory.RevertDatabase();
excelWorksheet.Name = "Part Sheet";
string[] strColumns = Constants.strExcelPartHeaders;
CreateSheetHeader(excelWorksheet, strColumns);
System.Drawing.Color clrPink = System.Drawing.Color.FromArgb(203, 192, 255);
System.Drawing.Color clrGreen = System.Drawing.Color.FromArgb(100, 225, 137);
string[] strValuesAndTitles = {/*...Column Names...*/};
List<string> lstColumns = strValuesAndTitles.ToList<string>();
System.Data.DataSet ds = _dataFactory.GetDataSet(Queries.strExport);
ADODB.Recordset rs = ConvertToRecordset(ds.Tables[0]);
excelRange = excelWorksheet.get_Range("A2", "ZZ" + rs.RecordCount.ToString());
excelRange.Cells.CopyFromRecordset(rs, rs.RecordCount, rs.Fields.Count);
int nFieldCount = rs.Fields.Count;
for (int nCounter = 0; nCounter < rs.RecordCount; nCounter++)
{
int nRowCounter = nCounter + 2;
List<ReportRecord> rrPartReports = _lstReports.FindAll(rr => rr.PartID == nCounter).ToList<ReportRecord>();
excelRange = (Excel.Range)excelWorksheet.get_Range("A" + nRowCounter.ToString(), "K" + nRowCounter.ToString());
excelRange.Select();
excelRange.NumberFormat = "#";
if (rrPartReports.Count > 0)
{
excelRange.Interior.Color = System.Drawing.Color.FromArgb(230, 216, 173).ToArgb(); //Light Blue
foreach (ReportRecord rr in rrPartReports)
{
if (lstColumns.Contains(rr.Title))
{
excelRange = (Excel.Range)excelWorksheet.Cells[nRowCounter, lstColumns.IndexOf(rr.Title) + 1];
excelRange.Interior.Color = rr.Description.ToUpper().Contains("TAG") ? clrGreen.ToArgb() : clrPink.ToArgb();
if (rr.Description.ToUpper().Contains("TAG"))
{
rs.Find("PART_ID=" + (nCounter + 1).ToString(), 0, ADODB.SearchDirectionEnum.adSearchForward, "");
excelRange.AddComment(Environment.UserName + ": " + _dataFactory.GetTaggedPartPrevValue(rs.Fields["POSITION"].Value.ToString(), rr.Title));
}
}
}
}
if (nRowCounter++ % 500 == 0)
{
progress.ProgressComplete = ((double)nRowCounter / (double)rs.RecordCount) * (double)100;
Notify();
}
}
rs.Close();
excelWorksheet.Columns.AutoFit();
progress.Message = "Done Exporting to Excel";
Notify();
_dataFactory.RestoreDatabase();
}
Can you use ODBC?
''http://www.ch-werner.de/sqliteodbc/
dbName = "c:\docs\test"
scn = "DRIVER=SQLite3 ODBC Driver;Database=" & dbName _
& ";LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;"
Set cn = CreateObject("ADODB.Connection")
cn.Open scn
Set rs = CreateObject("ADODB.Recordset")
rs.Open "select * from test", cn
Worksheets("Sheet3").Cells(2, 1).CopyFromRecordset rs
BTW, Excel is quite happy with HTML and internal style sheets.
I have used the Excel XML file format in the past to write directly to an output file or stream. It may not be appropriate for your application, but writing XML is much faster and bypasses the overhead of interacting with the Excel Application. Check out this Introduction to Excel XML post.
Update:
There are also a number of libraries (free and commercial) which can make creating excel document easier for example excellibrary which doesn't support the new format yet. There are others mentioned in the answers to Create Excel (.XLS and .XLSX) file from C#
Excel has the facility to write all the data from a ADO or DAO recordset in a single operation using the CopyFromRecordset method.
Code snippet:
Sheets("Sheet1").Range("A1").CopyFromRecordset rst
I'd normally recommend using Excel to pull in the data from SQLite. Use Excel's "Other Data Sources". You could then choose your OLE DB provider, use a connection string, what-have-you.
It sounds, however, that the real value of your code is the formatting of the cells, rather than the transfer of data.
Perhaps refactor the process to:
have Excel import the data
use your code to open the Excel spreadsheet, and apply formatting
I'm not sure if that is an appropriate set of processes for you, but perhaps something to consider?
Try this out:
http://office.microsoft.com/en-au/excel-help/use-microsoft-query-to-retrieve-external-data-HA010099664.aspx
Perhaps post some code, and we might be able to track down any issues.
I'd consider this chain of events:
query the SQLite database for your dataset.
move the data out of ADO.NET objects, and into POCO objects. Stop using DataTables/Rows.
use For Each to insert into Excel.