Using EPPlus library to replace formulas by their values in Excel - c#

I've read that Microsoft.Office.Interop.Excel would be the easiest way to replace formulas by their values in Excel but it requires to have Office installed. Since I will need to deploy on a Windows Server (2008 or 2012), I am looking for the best and/or simplest way to accomplish that using EPPlus.
Adding formulas is well documented, e.g.
currentWorksheet.Cells["C4"].Formula = "SUM(C2:C3)";
But I cannot find any example of replacing entire worksheets of formulas by their equivalent values. Basically the Copy followed by the Paste Special option in Excel.

I dont think there is any kind of function built into Epplus that will do that for you en masse. But you can take advantage of the fact that the Cells collection of the Worksheet only contains entries for cells with content. So something like this should not be too painful performance-wise:
currentWorksheet.Cells["C2"].Value = 5;
currentWorksheet.Cells["C3"].Value = 15;
currentWorksheet.Cells["C4"].Formula = "SUM(C2:C3)";
currentWorksheet.Cells["D2"].Value = 15;
currentWorksheet.Cells["D3"].Value = 25;
currentWorksheet.Cells["D4"].Formula = "SUM(D2:D3)";
//Calculate the formulas and the overwrite them with their values
currentWorksheet.Cells.Calculate();
foreach (var cell in currentWorksheet.Cells.Where(cell => cell.Formula != null))
cell.Value = cell.Value;

I know this is 3 years old, but if you're reading this now, EPPlus now has the function .ClearFormulas() that does just that, simply do
yourSheetHere.Calculate();
yourSheetHere.ClearFormulas();
and you're good to go.

Related

Setting style in cells with conditional formatting in ClosedXML C#

There was a need to make conditional formatting of a cell with a histogram. Used ClosedXML but it didn't give the desired result.
It is necessary to solve the problem with both the gradient and negative numbers. Has anyone encountered something similar? I am attaching the code.
form_sheet.Cell("D37")
.AddConditionalFormat()
.DataBar(XLColor.FromArgb(68, 114, 196), false)
.Minimum(XLCFContentType.Number, -3)
.Maximum(XLCFContentType.Number, 3);
Ready to consider alternative solutions not through ClosedXML. The program will generate several dozen reports. All histograms will be in the same cells, so I also considered vbs, but I don’t have enough experience to write such a script that would change styles immediately for a bunch of documents.
Bit late to answer, might be helpful for others..
I also gone through the same gradient issue. Currently using ClosedXML it is not possible to generate conditional DataBar with solid color.
What am doing to resolve my issue is to generate the Excel as of now with ClosedXML and re-open the Excel again in Interop and add the DataBar in the respective cells using Interop.Excel.
I haven't fully rewrote the code using interop because performance wise we can't fully rely on interop as compared to ClosedXML, atleast for me.
Sample code for adding Databar using Interop
var excel = new Microsoft.Office.Interop.Excel.Application();
var workBooks = excel.Workbooks;
var workBook = workBooks.Add();
var workSheet = (Microsoft.Office.Interop.Excel.Worksheet)excel.ActiveSheet;
workSheet.Cells[1, "A"] = 10;
Microsoft.Office.Interop.Excel.Range range1 = workSheet.Cells[1, 1];
Microsoft.Office.Interop.Excel.Databar bar = (Microsoft.Office.Interop.Excel.Databar)range1.FormatConditions.AddDatabar();
bar.BarFillType = Microsoft.Office.Interop.Excel.XlDataBarFillType.xlDataBarFillSolid;
Thanks.

Most effective way to duplicate an Excel worksheet?

I am working on a project upgrading a WPF program that has to work with Excel sheets a lot. It's using Closedxml and Excel interop to manipulate Excel files and add data.
After some trace I found a function that's painfully slow. It use the same method as other similar function, but everything else is working quite fine. The problem is Closedxml.copyto() eats up 85% of processing power.
It's purpose simply is to take one Excel sheet as a template if there is new record that need to be printed. it will copy the first sheet to new sheet then write data into it.
If you have any idea on how to speed up this kind of process, Please let me know!
foreach (object[] row in rows)
{
if (Common.integer(row[0]) < from_no || Common.integer(row[0]) > to_no)
{
continue;
}
sheetNum++;
if (sheetNum != 1)
{
this part use 85% of process Power
sheet_edit.CopyTo(sheetNum.ToString());
sheet_edit = book.Worksheet(sheetNum);
}
sheet_edit.Name = row[0].ToString();
ct.ThrowIfCancellationRequested();
w.ReportProgress(progCnt * 100 / maxCnt);
progCnt++;}
Thank you very much!
PS: sorry for my bad English!
PS: To anyone who did downvote my question, Please tell me the reason? Is it Not helpful or ... other reason?
PS: I searched all day but i cant find any answer for this. There are quite some method but those all just dont fit my need.
using interop: not quite faster.
using openxml: it mean i have write more code and it not quite easy to intergrade to this program
using closedXML.copyRange: Sure quite faster but it doesnt copy columns width, row height,... it mean more code, mode process... So not quite faster.
I decided to use dianogtics.process(print) in the loop, that 1st sheet will be reused in every loop. It kind of faster, but we cant choose printer or printer setting... default printer and setting will be used automatically.
I can explain this to my customer and i think this is quite aceptable.
But i am still waiting for the answer.. I you happened to know how to fasten this kind of processs up, please let me know!!
ClosedXML has to copy each object (cell, style, picture, etc) from the source to the destination. If you have many thousands of cells, then this will consume your CPU cycles.
You should ensure that your source worksheet contains only the cells and styles that you really need. In my experience, I have seen many Excel templates that contain many unused styles and empty cells at bizarre worksheet addresses.
If I were you, I would recreate the template as far as possible in ClosedXML itself (even if just a once-off process). This will ensure that your template is as minimal as possible. ClosedXML doesn't support all features yet, so after you create the template, you may want to add elements (e.g. charts). Then use that saved template in your further processing. It should be much smaller and faster as the one you're using now (my guess).
Other options you could try: An .xlsx file is just a .zip package. You can look at the underlying XML inside the file and determine how many cells or styles there are to be copied.
You can also download the ClosedXML source and narrow down exactly which kind of element is taking up the resources.
Disclaimer: I'm a ClosedXML project maintainer.
Instead of using ClosedXml to copy the sheet , You can use the Excel Interop to do the same. Below is the sample code for copying the worksheet
Excel.Application xlApp = Marshal.GetActiveObject("Excel.Application") as Excel.Application;
Excel.Workbook xlWb = xlApp.ActiveWorkbook as Excel.Workbook;
Excel.Worksheet xlSht = xlWb.Sheets[1];
xlSht.Copy(Type.Missing, xlWb.Sheets[xlWb.Sheets.Count]);
xlWb.Sheets[xlWb.Sheets.Count].Name = "NEW SHEET";

Epplus formula error

I'm using Epplus to put a formula into a cell. If I put this formula manually into the Excel cell it works:
=SUM(E4;G4)
But when I put in code with Epplus it doesn't work:
xls.ActiveSheet.Cells(8, 4).Formula = "SUM(E4;G4)"
Is there something special needed when I SUM two cells?
If I do the same with a range of cells it works, but with specific cells not.
This works (Range):
xls.ActiveSheet.Cells(8, 4).Formula = "SUM(E4:G4)"
Try changing this
xls.ActiveSheet.Cells(8, 4).Formula = "SUM(E4;G4)";
into
xls.ActiveSheet.Cells(8, 4).Formula = "SUM(E4,G4)";
Somehow using ; in a formula messes things up. Could be a bug...

C# EPPLUS excel array formula workarounds

The current version of EPPLUS support the creation of excel formulas but NOT excel array formulas, despite having the CreateFormulaArray() method.
When using the CreateFormulaArray() method, the correct formula string will appear on the excel formula editor. However, the formula does not actually execute on the sheet.
I was wondering if anyone knew of any clever workaround to this without having to use Microsoft.Office.Interop
My code is:
using (ExcelPackage pck = new ExcelPackage(newFile))
{
pck.Workbook.Worksheets.Add("Summary");
pck.Workbook.Worksheets.MoveToStart("Summary");
var summaryWS = pck.Workbook.Worksheets[1];
summaryWS.Cells["C2"].Value = 2;
summaryWS.Cells["C3"].Value = 3;
summaryWS.Cells["C4"].Value = 8;
summaryWS.Cells["A1"].CreateArrayFormula("STDEV.P($C$2:$C$4)*SQRT(8*260)");
}
my output in excel would be #NAME?
The formula editor would show {=STDEV.P($C$2:$C$4)*SQRT(8*260)}
Seems Excel is misinterpreting the function name STDEV.P which is the newer version of STDEVP. If you look at the XML output AFTER opening and saving with excel the wb EPPlus generates you will see it says _xludf.STDEV.P which means it thinks it is user-defined.
You can do one of two things. You could use the old version of the function:
summaryWS.Cells["A1"].CreateArrayFormula("STDEVP($C$2:$C$4)*SQRT(8*260)");
which is probably less then ideal since you always want to stick with the latest version. In that case, force excel to recognize the function like this:
summaryWS.Cells["A1"].CreateArrayFormula("_xlfn.STDEV.P($C$2:$C$4)*SQRT(8*260)");

Calculating through Excel sheet

I got an Excel sheet with some formulas in them. What I'm trying to do is fill in a formula and let Excel calcute it, then return to me the outcome.
Let's say my formula ( which is in cell A3) look like this:
=SUM(A1+A2)
What I would like to do is fill in A1 and A2 from my c# code. Than read the outcome that is in A3.
However, since this is for a website, I want multiple users to be able to calcute at once. So I suspect really opening the file will take too many resources.
I've searched high and low and found this: SpreadsheetGear. I'm not sure, but I think that library should do the trick. Too bad it is way to expensive for the simple things I need to accomplish.
Is there a (preferable free) way to return the value that Excel calculates based of my formula?
Does that work?
Range r = sheet.get_Range("A1");
r.Value2 = 1;
r = sheet.get_Range("A2");
r.Value2 = 2;
r = sheet.get_Range("A3");
r.FormulaR1C1 = "=R[-2]C+R[-1]C";//=(A1+A2)
int A3_Value = (int)(r.Value2);

Categories