I have couple of formulas and data coming from database. I want to refresh all the formulas programmatically through ExcelDna. All these formulas are ExcelFunctions and I have put ExcelCommand name = "Refresh" on which i want to issue recalculate to excel sheet..
I use following but it refresh only native excel functions e.g. NOW(), SUM() etc. it does not invoke refresh on ExcelDna Functions ?
[ExcelCommand(MenuName="Refresh", MenuText="Refresh" )]
public static void GetPositionCommand()
{
XlCall.Excel(XlCall.xlcCalculateNow);
}
Thanks in advance...
xlcCalculateNow will only calculate formulae that Excel knows have to be recalculated. You can mark an Excel function as 'Volatile' for it to behave like Excel's NOW() function, which is recalculated every time. With Excel-DNA you can do this:
[ExcelFunction(IsVolatile=true)]
public static string MyVolatileNow()
{
return DateTime.Now.ToString("HH:mm:ss.fff");
}
and compare with the default non-volatile case:
[ExcelFunction]
public static string MyNow()
{
return DateTime.Now.ToString("HH:mm:ss.fff");
}
Another way to push data to Excel is to create an RTD server or use the new Reactive Extensions for Excel (RxExcel) support in the latest Excel-DNA check-ins. Some info here - http://exceldna.codeplex.com/wikipage?title=Reactive%20Extensions%20for%20Excel.
I have to hit Ctrl + Alt + F9 (at least in Excel 2013) to refresh the cells!
This works (at least in Excel 2013):
Excel.Application excelApp = ExcelDnaUtil.Application as Excel.Application;
excelApp.CalculateFull();
or
Excel.Application excelApp = ExcelDnaUtil.Application as Excel.Application;
excelApp.CalculateFullRebuild();
The first one recalculates all Workbooks (similar like you press Ctrl + Alt + F9), the second one is similar to reentering all formulas.
Try setting IsVolatile=false in the attribute for the ExcelDNA function:
[ExcelFunction(Description = "Subscribe to real time data.", IsVolatile = false, Category = UDF_CATEGORY)]
public static object XSub(
string param1)
{
// Implementation here.
}
This simple change dramatically drops CPU usage. In one big spreadsheet I was working with, CPU usage dropped from 100% (i.e. continuously recalculating, and never catching up) down to 20%.
With IsVolatile=true, Excel will recalculate the output value of the function regularly, even if the inputs did not change.
With IsVolatile=false, Excel will only recalculate the output value of the function if the inputs change.
With IsVolatile=false, everything is an order of magnitude more efficient, as Excel can skip recalculating 90% of the spreadsheet most of the time.
Having said this, some functions might need IsVolatile=true, for example, a function that returned the current time.
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";
In my current project, Aspose has been used to work with Excel file (XLXS). This excel file has 4 worksheets. First two sheets are empty except they have first row which contain column names. These tab got data through code and other two contains tons of complex formula based on these inputs. Just imagine first two tab as inputs, third tab as complex calculation and last tab as output. Average size of file ranges from 26MB to 48MB. Below piece of code does most of the work. After this method, the file has been saved in some physical location too. output date saved in DB. This process working fine so far with above range, but when size exceeded beyound 100MB, it started throwing Out of Memory exception. Hardly once or twice, it able to complete the process in around 80 - 100 mins.
public void CaclulateM(DataSet dataModel)
{
var workbook = this.ExcelModel.Workbook;
var ranges = ExcelModel.GetExcelModelRanges;
base.ImportInputsTo(workbook, ranges, dataModel);
workbook.CalculateFormula(false);
base.ExportOutputsTo(workbook, ranges, dataModel);
}
I tried out some of the solution provided by Aspose, but failed.I tried other dlls too including Interop, ExcelLibrary, NPOI, but same result.
https://forum.aspose.com/t/aspose-cell-dll-issue-for-xslb-file/164440
Please help or let me know if you need any other input to suggest anything. I cannot provide you the excel file due to confidentiality.
I want to break all links within an excel workbook using c# via a VSTO addin and convert the formulas to hardcoded data.
EDIT UPDATE:
Code iterates through each link fine but the link is not broken, the breaklink line executes but nothing happens?
Array links = (Array)((object)invoiceBook.LinkSources());
if (links != null)
{
foreach (string link in (Array)links)
{
invoiceBook.BreakLink(link, Excel.XlLinkType.xlLinkTypeExcelLinks);
}
}
Seems the wb.Breaklink function does not work with certain formulas - in my case SUMIFs. I was able to get the breaklink function to work by adding a named range with a value of 1 then adding to the end of my sumif formula * namedrange. Excel then recongnised the external link and removed it effectively
Excel formula get lost after saving, were replaced by last calculation result.
Question: Is there a special save option in order to keep formulas ?
rangeTarget = workSheetTeam.get_Range(workSheetTeam.Cells[40, BG], workSheetTeam.Cells[40, BG]);
rangeTarget.Formula = "=MAX(BF8:BF25)";
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.