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
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 was wondering whether someone encountered this before, suppose we have the following piece of code:
Excel.Range vl_range = (Excel.Range)((Excel.Worksheet)Application.ActiveSheet).get_Range("A1");
This safely returns the range of A1. However, if we input the string "'new sheet'!$C$1", then it gives a HRESULT error.
But, this can be solved by, the following piece of code:
Excel.Range vl_range = (Excel.Range)(Application.get_Range("'new sheet'!$C$1"));
That does seem to work. Now I was wondering if I input "A1" just in the last line of code, would it automatically assume that I'm referring to the active sheet?
Can someone explain to me, why this seems to be different in VBA? Where I can define ranges easily using the Range method, even though I am in a sheet?
Is my conclusion correct or am I making a horrible mistake somewhere?
For anyone who is interested. I have tested the code and if we use:
Excel.Range vl_range = (Excel.Range)(Application.get_Range("C1"));
Excel automatically assumes that you are referring to the Activesheet.
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)");
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).