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

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

Related

Excel VBA: Naming a range within a user defined function

I am trying to create a user defined function to create a named range and assign a value to the cell. The below code is giving me a #Value error
My code:
Public Function NameARange(CValue As String, NameR As String) as String
Dim ReferAdd As String
ReferAdd = "='" & ActiveSheet.Name & "'!" & ActiveCell.Address
ActiveWorkbook.Names.Add Name:=NameR, RefersTo:=ReferAdd
NameARange = CValue
End Function
Any help to fix this code will be much appreciated. Thanks
Are you sure it's not a #NAME? error that you're getting?
Either way, there are a couple issues with your formula. The short explanation is it doesn't make logical sense.
Let's say you stick that formula in cell A1 of Sheet1...
You're trying to create a named range with a Worksheet Function. Worksheet functions recalculate (re-execute) every time something changes on the worksheet. Excel would try to recreate a new named range by the existing name, over and over and over.
Imagine if you had to sweep the floor anytime sometimes changed in your house... but sweeping the floor changes your house. You'd be stuck in an infinite loop.
You're also want the function to assign a formula to the cell that the function is sitting in. What if you could clone yourself, but the only place that clone could ever stand is exactly where you are standing. Wouldn't work out.
And, finally, you want to finish by returning a value to the same cell that has the function (and the infinite copies of itself)... but not just any value: the value that you called the function with in the first place.
It's like a Catch-22 of a Quagmire of a Paradox.
There is no solution for what you're trying to do except, "don't". Excel won't let you anyhow, which is good because otherwise the universe just might implode.
A user-defined function called by a formula in a worksheet cell cannot
change the environment of Microsoft Excel. This means that such a
function cannot do any of the following:
Insert, delete, or format cells on the spreadsheet.
Change another cell's value.
Move, rename, delete, or add sheets to a workbook.
Change any of the environment options, such as calculation mode or screen views.
Add names to a workbook.
Set properties or execute most methods.
The purpose of user-defined functions is to allow the user to create a custom function that is not included in the functions that ship with Microsoft Excel. The functions included in Microsoft Excel also cannot change the environment. Functions can perform a calculation that returns either a value or text to the cell that they are entered in. Any environmental changes should be made through the use of a Visual Basic subroutine.
During calculation, Excel examines the precedents of the cell that contains a user-defined function. If not all precedents have been calculated so far during the calculation process, Excel eventually calls the user-defined function and passes a Null or Empty cell to the function. Excel then makes sure that enough calculation passes occur for all precedents to be calculated. During the final calculation pass, the user-defined function is passed the current values of the cells. This can cause the user-defined function to be called more frequently than expected, and with unexpected arguments. Therefore, the user-defined function may return unexpected values.
For correct calculation, all ranges that are used in the calculation should be passed to the function as arguments. If you do not pass the calculation ranges as arguments, instead of referring to the ranges within the VBA code of the function, Excel cannot account for them within the calculation engine. Therefore, Excel may not adequately calculate the workbook to make sure that all precedents are calculated before calculating the user-defined function.
(Source: Microsoft : Description of limitations of custom functions in Excel)

Excel formula within VBA

I have quite a few cells that contain formula, then with VBA the outcome of this formula is the value for a variable, like so:
On sheet in cell AS4:
=SUMPRODUCT(MAX((ROW($AE$4:$AE$997))*($AE$4:$AE$997<>"")))
and then in my VBA:
numRows = ws.Range("AS4").Value
However this is starting to get hard to keep track of which cell is feeding which variable, avoiding overwriting those cells on the sheet by accident, etc.
I need to be able to perform this calculation within VBA if I can, removing the need to have "calculation cells" on my sheet.
I have discovered there is a way to use formula with WorksheetFunction, but only found simple examples of this and cannot adapt it to my situation above.
numRows = WorksheetFunction.SumProduct(MAX((ROW($AE$4:$AE$997))*($AE$4:$AE$997<>"")))
Is not going to work...
Is there a way to do this, or am I better scrapping the idea of using formula and using a pure VBA method?
With help from SJR this was the answer:
numRows = [=SUMPRODUCT(MAX((ROW(Weights!$AE$4:$AE$997))*(Weights!$AE$4:$AE$997<>"""")))]
A bit more research taught me that evaluate(" ") can be just replaced with square brackets [ and ]. Although, if I had variables in the mix of this formula or the formula wasn't constant then I would have to use Evaluate.
I also needed to add the sheet name to the formula as this formula was no longer functioning within the sheet and AE4:AE997 was no longer referring to the correct sheet.
Doubling up on quotes is also necessary as it is code and sees " differently to a formula on the sheet

Convert Excel Formula to VBA

I have this formula that looks at various criteria across multiple columns and checks to see that if all the all the criteria match, it will paste data from one column to another. I've tried a couple ways to get it into VBA, but I can't seem to get anything to work. Thanks!
=INDEX($D$2:$D$1112,MATCH(1,($A$2:$A$1112=$U$7)*($C$2:$C$1112=$W$7)*($B$2:$B$1112=F3),0))
You are not going to be able to use that array formula to directly return a value to a cell. VBA does not process an array formula the way that the worksheet can. The best method is to use the worksheet's processing or one of the Application Evaluate methods.
Your lack of a worksheet to reference troubles me. When a formula is in a worksheet cell, it knows what worksheet it is on. When using formulas within VBA, the parent worksheet is a 'best guess' without explicit worksheet referencing.
Here are three methods to put the results from that array formula into Z2:Z4 on the active worksheet. Remember that these cell references should be modified to include the worksheet name.
With ActiveSheet
'this simply puts the formula into the worksheet then reverts the cell from the formula to the returned formula value
.Range("Z2").FormulaArray = "=INDEX($D$2:$D$1112, MATCH(1, ($A$2:$A$1112=$U$7)*($C$2:$C$1112=$W$7)*($B$2:$B$1112=F3), 0))"
.Range("Z2") = .Range("Z2").Value
'this uses the 'square bracket' method of evaluating a formula on-the-fly
'the formula being evaluated can be array or non-array
'this method is does not like building a formula string from pieces of text
.Range("Z3") = [INDEX($D$2:$D$1112, MATCH(1, ($A$2:$A$1112=$U$7)*($C$2:$C$1112=$W$7)*($B$2:$B$1112=F3), 0))]
'similar to the method directly above, Application.Evaluate does just that.
'the formula being evaluated can be array or non-array
'this method is easier to build a formula string from pieces of text
.Range("Z4") = Application.Evaluate("INDEX($D$2:$D$1112, MATCH(1, ($A$2:$A$1112=$U$7)*($C$2:$C$1112=$W$7)*($B$2:$B$1112=F3), 0))")
End With
You need 2 changes:
(1) To use a function in VBA when it is available in native Excel, you need to preface each function with Application.WorksheetFunction. ie:
x = Application.WorksheetFunction.Sum(y,z)
(2) To reference a cell within a sheet, in VBA, you need to access it specifically, in one of a few ways. The simplest for our purposes is the RANGE property, as follows:
x = Application.WorksheetFunction.Sum(Range("A1:A2"))
So to put those two changes together, your formula would look like this:
=Application.WorksheetFunction.INDEX(Range("$D$2:$D$1112",Application.WorksheetFunction.MATCH(1,(RANGE("$A$2:$A$1112"=RANGE("$U$7")*(Range("$C$2:$C$1112"=Range("$W$7")*(Range("$B$2:$B$1112"=Range("F3"),0))
Although I see now having gone through this that you seem to be using an Array Formula - not sure if any special jigging is required to get that to work.

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.

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.