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)");
Related
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.
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";
I'm trying to change the formatting for all Named Ranges in an Excel file but I'm getting an error: "unable to set the name property of the font class".
Sorry if this has been asked but I could not find any.
Here's my code. I'm sure I'm just missing something.
Thanks in advance!
Excel.Application xlsx = new Excel.Application();
Excel.Workbook wb = xlsx.Workbooks.Open(filepath, ReadOnly: false, Editable: true);
foreach (Excel.Name NamedRange in wb.Names)
{
//MessageBox.Show(NamedRange.Name.ToString());
NamedRange.RefersToRange.Cells.Font.Name = "Arial";
}
xlsx.Application.ActiveWorkbook.Save();
xlsx.Application.Quit();
xlsx.Quit();
When you see any error that resembles that one, it probably indicates that either the cell, the worksheet or the workbook itself is protected. Try to select the range manually (within Excel, not C#) and change the Font. My guess is you will get a similar error.
There are ways to unprotect within C#, but before heading down that path, evaluate the use case and see if that makes sense for what you are trying to do.
Or, check to see if the range is protected before executing your command. Something similar to this should work.
if (!NamedRange.RefersToRange.Locked)
NamedRange.RefersToRange.Cells.Font.Name = "Arial";
On a side note, Excel has pretty weak protection (by design), there are ways to hack the protection. Here is one example:
Alternatives to try/catch in VSTO
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.
I have data in an Excel spreadsheet with values like this:
0.69491375
0.31220394
The cells are formatted as Percentage, and set to display two decimal places. So they appear in Excel as:
69.49%
31.22%
I have a C# program that parses this data off the Clipboard.
var dataObj = Clipboard.GetDataObject();
var format = DataFormats.CommaSeparatedValue;
if (dataObj != null && dataObj.GetDataPresent(format))
{
var csvData = dataObj.GetData(format);
// do something
}
The problem is that csvData contains the display values from Excel, i.e. '69.49%' and '31.22%'. It does not contain the full precision of the extra decimal places.
I have tried using the various different DataFormats values, but the data only ever contains the display value from Excel, e.g.:
DataFormats.Dif
DataFormats.Rtf
DataFormats.UnicodeText
etc.
As a test, I installed LibreOffice Calc and copy/pasted the same cells from Excel into Calc. Calc retains the full precision of the raw data.
So clearly Excel puts this data somewhere that other programs can access. How can I access it from my C# application?
Edit - Next steps.
I've downloaded the LibreOffice Calc source code and will have a poke around to see if I can find out how they get the full context of the copied data from Excel.
I also did a GetFormats() call on the data object returned from the clipboard and got a list of 24 different data formats, some of which are not in the DataFormats enum. These include formats like Biff12, Biff8, Biff5, Format129 among other formats that are unfamiliar to me, so I'll investigate these and respond if I make any discoveries...
Also not a complete answer either, but some further insights into the problem:
When you copy a single Excel cell then what will end up in the clipboard is a complete Excel workbook which contains a single spreadsheet which in turn contains a single cell:
var dataObject = Clipboard.GetDataObject();
var mstream = (MemoryStream)dataObject.GetData("XML Spreadsheet");
// Note: For some reason we need to ignore the last byte otherwise
// an exception will occur...
mstream.SetLength(mstream.Length - 1);
var xml = XElement.Load(mstream);
Now, when you dump the content of the XElement to the console you can see that you indeed get a complete Excel Workbook. Also the "XML Spreadsheet" format contains the internal representation of the numbers stored in the cell. So I guess you could use Linq-To-Xml or similar to fetch the data you need:
XNamespace ssNs = "urn:schemas-microsoft-com:office:spreadsheet";
var numbers = xml.Descendants(ssNs + "Data").
Where(e => (string)e.Attribute(ssNs + "Type") == "Number").
Select(e => (double)e);
I've also tried to read the Biff formats using the Excel Data Reader however the resulting DataSets always came out empty...
The BIFF formats are an open specification by Microsoft. (Note, that I say specification not standard). Give a read to this to get an idea of what is going on.
Then those BIFF you see correspond to the some Excel formats. BIFF5 is XLS from Excel 5.0 and 95, BIFF8 is XLS from Excel 97 to 2003, BIFF12 is XLSB from Excel 2003, note that Excel 2007 can also produce them (I guess Excel 2010 too). There is some documentation here and also here (From OpenOffice) that may help you make sense of the binary there...
Anyways, there is some work has been done in past to parse this documents in C++, Java, VB and for your taste in C#. For example this BIFF12 Reader, the project NExcel, and ExcelLibrary to cite a few.
In particular NExcel will let you pass an stream which you can create from the clipboard data and then query NExcel to get the data. If you are going to take the source code then I think ExcelLibrary is much more readable.
You can get the stream like this:
var dataobject = System.Windows.Forms.Clipboard.GetDataObject();
var stream = (System.IO.Stream)dataobject.GetData(format);
And read form the stream with NExcel would be something like this:
var wb = getWorkbook(stream);
var sheet = wb.Sheets[0];
var somedata = sheet.getCell(0, 0).Contents;
I guess the actual Office libraries from Microsoft would work too.
I know this is not the whole tale, please share how is it going. Will try it if I get a chance.