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.
Related
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'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.
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)");
My requirement to change background color of cells in a row of a excel sheet.
ex : if A1 cell value is less than 100, i need to show it in Red Background.
I searched alot, i found some code to create stylesheets from this
http://blogs.msdn.com/b/chrisquon/archive/2009/11/30/stylizing-your-excel-worksheets-with-open-xml-2-0.aspx
that code is using StyleIndex property. And its very complex.
Is there any other solution...???????
For manipulating spreadsheets in OpenXML format there are several wrappers around the raw SDK that make things much simpler, e.g.
ClosedXML
EPPlus
SpreadsheetLight
Using ClosedXML you could use conditional formatting to achieve your desired result (see documentation):
using (var wb = new XLWorkbook())
{
using (var ws = wb.AddWorksheet("Test"))
{
ws.Cell("A1").Value = 42;
ws.Cell("A1").AddConditionalFormat().WhenLessThan(100)
.Fill.SetBackgroundColor(XLColor.Red);
}
wb.SaveAs(#"C:\Dev\Test.xlsx");
}
What is the C# equilivant to this VB6 to setting of the active cell?
ActiveSheet.Range("L1").Select
Here's a sample piece of code:
Excel.Worksheet sht = (Excel.Worksheet)ActiveSheet;
sht.Cells[3, 3] = "HELLO";
You can also capture ranges:
Excel.Range rng = (Excel.Range)sht.Cells[3, 3];
I believe to you just the Select method as before to select a range, although I haven't tested this.
rng.Select();
You can obviously streamline this and chain these statements together, with the right casting. I don't want to hazard a guess here as I've not got a VSTO project open in from of me.
EDIT
You should also be able to get a range from the sheet using get_Range:
rng = sht.get_Range("A1", Type.Missing);
VSTO tends to return Objects most of the time, necessitating casts, but get_Range is an exception. Someone might be able to correct me as I am not a big user of VSTO (still VBA die-hard when it comes to Excel).