EPPlus - custom function in C# - handling cell dependencies outside of the spreadsheet - epplus

What i'm doing: added a custom function to the EPPlus runtime. This function takes a text expression as an argument (of course the text expression can be specified as an argument or can be put in some cell of the spreadsheet that we refer)
So far, so good
then we evaluate this expression outside of Excel/EPPlus - the expressions can access data outside the spreadsheet BUT ALSO can refer to other spreadsheet cells.
And there is problem with referring back to the spreadsheet - EPPlus has no idea what's in these expressions and what will be the result of their evaluation, also doesnt know if they refer to other cells that may or may not be already calculated. So - no dependency control and possible mess.
Question: can this be fixed? I mean, when the expression evaluating function detects that the expression refers to a spreadsheet cell, can it tell EPPlus to evaluate that cell first? Or at least parse the expression and detect what are the dependencies, then give this information somehow to EPPlus so it knows in what order to run the calculations?
=MYEVAL("doSomeStuff('arg 1', Sheet.E4, Sheet.C4)")
MYEVAL is going to be run during Worksheet.Calculate() call - but inside MYEVAL, how to tell EPPlus that we want the current value of E4 and C4 cells, if these cells are calculated themselves and can change their value during calculation?
I dont care about cyclic references, can make sure there are no cycles, just the straightforward case of running the calculations in correct order.
Update: just wondering, if we can add any number of arguments to the call, could we just put our dependencies in the arguments list, like this:
=MYEVAL("doSomeStuff('arg 1', Sheet.E4, Sheet.C4)";E4;C4)
Then EPPlus would know that before calling MYEVAL it has to calculate E4 and C4 cells. Is this enough for enforcing such dependencies?

OK the solution presented in the Update is OK/useful and appears to work.
But there is also a very simple approach, alternative: you can calculate individual cells using the EPPlus api (Worksheet.Cells[i, j].Calculate()) - then you dont need to manage the dependencies, just call Calculate on the cells you need and this will follow actual dependencies.

Related

Named Cells and Formulas In Excel

How do I utilize named cell references in Excel that aren't absolute. I want to be able to take a formula and be able to drag it across excel and have one name cell reference update to a different named cell as I move across.
For example: I want to keep RevenuePerStay going across the formulas row and have excel updated the cell reference to the number of people staying. So
400 should be RevenuePerStay * Stay400
600 should be `RevenuePerStay * Stay600`
I tried using mixed cell reference and relative cell references using the dollar sign but excel will not accept this.
Assuming your stays are in row 5:
For this worksheet, click on cell C7 and go to create a new named range called Stays and for the formula write =C$5$ and exit the name manager.
Now change your formula in C7 to being RevenuePerStay*Stays and drag it across. This will get the right amount of stays you want each time.
In explicit answer to your question: no you would never get the name in the formula to change unless you put all scenarios in the formula using multiple if statements.
If I understand your question correctly, this method seems convoluted because you can use =RevenuePerStay*C5 and drag over the row, and it should give the answer you want.
If you really want to take the advantage of named range and make it change dynamically, you will need to incorporate with INDIRECT like this:
=RevenuePerStay*INDIRECT("Stay"&C5)
But this is assuming you have all the named ranges defined properly such as Stay200, Stay400, Stay600, Stay800, Stay1000 like below. Otherwise it will not work.

Why does Excel stop a macro when using Application.Evaluate?

I'm trying to use the Application.Evaluate function to test if a conditional formatting condition is true. However, what is happening is that the macro just stops - no error message, and the cell in which the UDF is referenced returns #VALUE.
The value of the conditional formula Formula1 property in this instance is "=A1<>VLOOKUP($A1,actWOrders1!$A:$EF,COLUMN(A1),FALSE)"
I've tried replacing Application.Evaluate with ActiveWorksheet.Evaluate, in case it is the Application form is struggling with the context, but the same happens.
Any ideas what might be causing the issue?
Thanks! Screwtape.
If your macro doesn't contain any on error handing statements it would certainly provide you with an error message if at any point it was unable to execute. It could be that your macro is executing completely however is not achieving the result you expect.
Try running it step by step using F8 and observing what is happening on each line to find the culprit, as you step through you can hover your cursor over a number of items such as variables to see what their value is. You might also find reading this webpage will help you to use the tools in VBA to debug your macros.
You created a circular reference.
If you try and type the formula into a cell Excel will tell you that has a circular reference.
You can also step through a formula in the formula bar.
Highlight an expression in the formula that you want the value of
Press [F9] to calculate the expression
The expression will be replaced with its value

Evaluate a relative reference from VBA relative to a known cell

I'm using the trick described here - !A1 - to get the the range of cells up to and including the current one (typically for a Rows() function). This works great for day to day usage, and eliminates a lot of errors that I get when moving ranges around when I previously had to use an adjacent set of rows.
Unfortunately, my formulas need to be evaluatable from VBA. With __THISCELL__ as my !A1 cell, and the cell housing the formula as $Z$100 the following evaluates to an error:
Application.Evaluate(rngCell.formula)
And the following evaluates to $A$1:$Z$50
rngCell.Worksheet.Evaluate(rngCell.formula)
Obviously an approach is to replace __THISCELL__ with rngCell.Address(External:=True) prior to evaluation, but here's the kicker: I'd like to be able to execute my formula parser in a workbook which uses, say THIS_CELL, THISCELL or __THISCELL safely, and I'd also like to be able to safely execute my code in a workbook with a name like __NOT__THIS_CELL__.
All I need for this is a mechanism to evaluate relative references relative to a specific cell address - which since people do use R1C1 references in VBA a fair bit, I imagine must be around. However, I don't know it. Can anyone help?
NB: I like to avoid fiddling with ActiveCell, Selection, etc. where possible, since those smell like the excel equivalent of SendKeys - who knows what the user is doing when you access them. Even then, though, I'm not certain I'll get the right answer, because for the Worksheet.Evaluate approach, I'm not positioned in cell $A$1!
If I understand your question, I believe you're looking for the Range().Offset method.
Range().Offset(rOffset, cOffset) refers to a range that is rOffset lower and cOffset to the right of the given range (negative values for up and left are allowed). Also, .Offset can access and set all of the properties of the range, just like you would do with .Range.
The approach I've taken for the time being is implicit in the question: when a named range is detected, store the current selection and worksheet, select the one which we use as the evaluation context, and then use Evaluate. This seems to work, provided the cell being evaluated is inside the activesheet.
I don't like jumping the selection all over the place - feels dirty - but short of a more elegant solution, it does work.

How can I create sheet-specific named ranges without using Indirect?

I've come up with a useful trick, where I create a named range that refers to the current worksheet, by using the following formula:
=RIGHT(CELL("filename",INDIRECT("A1")), LEN(CELL("filename",INDIRECT("A1"))) - FIND("]",CELL("filename",INDIRECT("A1"))))&T(NOW())
Where the INDIRECTs are there ONLY to stop Excel from Converting A1 --> Sheet1!A1. This works beautifully until I need to call evaluate on it from VBA (which does happen).
Can anyone tell me how either (1) to evaluate a name with this formula in VBA or (2) to get a sheet non-specific reference into the formula. I'd rather not use VBA, since it'll get evaluated ~12000 times, and that's likely to be slow, but if need be, it's probably ok. However, please bear in mind that the sheet it is calculated from is quite unlikely to be ActiveSheet, so the context for the Range() function in VBA is a little tricky - hence why I'm asking in the first place.
One possible approach: use a simple UDF which just returns the name of the sheet it's called from.
Eg:
Function SheetName()
SheetName = Application.Caller.Parent.Name
End Function

Excel VBA UDF that return value will override itself

Can I write a UDF in Excel VBA where the return value from the function will override the cell value from it is called from?
The function get information with a sql request. In this case it's only master data for example the item description. If the user will use this function in a worksheet in many cells excel will recalculate the cell value every time you change something. This has poor performance and normally it's only necessary to get the information one time and it hasn't to be updated in this case.
I thought to use application.caller.address method to get the address the function was called from but it seems it can't set the cell value for this address within the function.
So the return value of the function should override the original formula that run the function.
Is this possible
thanks for your help
No.
As you may have noticed Excel cells have multiple layers.
One is the "value". Another one the formula you can assign.
A funtions returns a value, therefore the return value only accesses this layer. So you cannot return a replacement for the formula cause it is on another layer.
A function differs from a sub in the return value, a sub does not return anything. Due to your behaviour of "one time usage" a sub will fit your need more than a function, because you dont want to return a value but to remove or replace certain content from cell (the formula).
However, this does not mean you cannot do this with a function - but still not with a return value. But you need to rewrite the whole formula on a data refresh if you would use such a function.
You may have missed a point that you make you laugh yourself I guess. Excel has such a thing natively. But it is not a function.
Copy your cells and paste them but use "values only".
Totally has the same effect.
Also in terms of recalculation... why not turn it off?
This would you not make to rewrite the function each time.