I'm trying to use range.Find to find the cell in another sheet that contains a string input on a previous sheet (I'm searching for a column header in a table so I won't have to update my macro if columns get shifted around). I'm using the function below, but I always get an error saying that an Object is Required. I've looked at similar topics and tried their methods, but what works for them just isn't working for me. How can I use Range.Find to find the cell that a column header is in in a different sheet? Here's the code I'm using:
Function FindColumn(ByVal name as String) As Range
'cds is the other worksheet I need to find the column header in
Set FindColumn = cds.Range("A1:AA1").Find(name)
If FindColumn Is Nothing Then MsgBox(name & " Not Found!")
Exit Function
End Function
in your code, cds would appear to be Nothing, which could contribute to that error. Is cds assigned a worksheet object elsewhere in your code? If so, what is that variable's scope? If it is not module or public, then that would explain it.
If it is module/public, ensure that the variable cds is assigned prior to calling the function.
If it is a procedure-level variable, you will need to pass it to the function like:
FindColumn "columnname", cds
And modify the function to accept this additional argument:
Function FindColumn(byVal name as String, cds as Worksheet)
Related
I am creating a class module that I would like to be used with minimal setup/preparation as I expect it to be used within multiple projects. One of the requirements is that the class module should first check for a special worksheet that is to be hidden named _SYS that will store property values from the class so they may persist between sessions.
One way I was hoping to accomplish this task was by having the class create named ranges on the fly. This shouldn't be much of an issue using WorkbookObj.Names.Add method, but I would like to know if a range has its own property that would allow me to set or retrieve its name as opposed to needing to loop through the collection of names.
My goal was to have the class loop through all names within a column in the hidden worksheet to determine the next available row to use to by determining if its name began with an underscore or not (_Name1, _Name2, A3 <- This would be the next available range to use).
I figured that Range.Name would have accomplished this for me, but it appears to just be returning an "extended address" instead of the name:
As you can see below, it successfully created the named range, but it will not return its name.
Here's essentially what I am attempting to do:
Private Function NextUnnamedCell() As Range
Dim Cell As Range
For Each Cell In Sheet1.Columns(4).Cells
If Not Left(Cell.Name, 1) = "_" Then
Set NextUnnamedCell = Cell
Exit Function
End If
Next Cell
End Function
The Name object has a Name property, so:
Cell.Name.Name
should give you the name, as long as Cell has a Name, otherwise you'll get an error.
I hope I'm asking this in the correct forum:
I'm writing a UDF in VBA for MS-Excel; it basically builds a status message for the transaction on that row. It steps through a series of IF statements, evaluating cell values in different columns FOR THAT ROW.
However, this UDF will reside in multiple rows. So it might be in C12, C13, C14, etc. How would the UDF know which row to use? I'm trying something like this, to no effect
Tmp_Row = Application.Evaluate("Row()")
which appears to return a null
What am I missing here ?
Thanking everyone in advance
Application.Caller is seldom used, but when a UDF needs to know who called it, it needs to know about Application.Caller.
Except, you cannot just assume that a function was invoked from a Range. So you should validate its type using the TypeOf...Is operator:
Dim CallingCell As Excel.Range
If TypeOf Application.Caller Is Excel.Range Then
'Caller is a range, so this assignment is safe:
Set CallingCell = Application.Caller
End If
If CallingCell Is Nothing Then
'function wasn't called from a cell, now what?
Else
'working row is CallingCell.Row
End If
Suggestion: make the function take its dependent cells as Range parameters (if you need the Range metadata; if you only need the values then take in Double, Date, String parameters instead) instead of making it fetch values from the sheet. This decouples the worksheet layout from the function's logic, which in turn makes it much more flexible and easier to work with - and won't need any tweaks if/when the worksheet layout changes.
Application.ThisCell
MS Docs:
Returns the cell in which the user-defined function is being called from as a Range object.
You can put it to the test using the following code:
Function testTC()
testTC = Application.ThisCell.Row
End Function
In Excel use the formula
=testTC()
and (Cut)Copy/Paste to various cells.
I have a table defined in Excel as a ListObject:
In VbA, I can access rows and columns using ListObject("table1").ListRows or ListObject("table1").ListColumns
In one of my subs, I wan't to know the active cell's column name. And because the columns could be moved around, I want this to be dynamic.
For example, in the previous image, let's say I clicked on a cell of the third column.
I'd like to have a function that tells me the column name associated with that cell in the current ListObject.
In this case it would return Line Desc.
I tried to use a spy to find an object path that would allow me to figure out the column name, but I couldn't find any.
Before I start building a function that will do just that, I wanted to make sure I didn't miss anything in VbA objects repository.
Any idea if there is a built-in way to find the active cell's column name?
Thanks
Note: If I run out of time and create a function, I will post it here.
EDIT
Here's what I have found so far:
Function ColumnName(byVal Target as Range)
If Sheets("Equipements").Listobjects("tMain").Active then
ColumnName = ListObjects("tMain").HeaderRowRange.Cells(1, Target.Column).Value
End If
End Function
Possible issue with your method occurs when the ListObject doesn't start from Column A.
Con: Fixed to the table "tMain" only, cannot use on any other tables.
You can try this (if Target is more than 1 cell, only the top left cell is used):
Option Explicit
Function ColumnName(ByRef Target As Range) As String
If Not Target.ListObject Is Nothing Then
ColumnName = Intersect(Target.ListObject.HeaderRowRange, Target.EntireColumn).Value
Else
ColumnName = ""
End If
End Function
I have a WS named Stats and I want to fill a column with the MINIMUMs. The data is in the same WB, but on another WS called Data.
The data is in rows, so MIN calc would be performed on range B to IQ columns.
And I need to calc the MIN for rows 14 to 1868.
The following code works, but name and range of the data is hard coded:
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN(Data!B58:IQ58)"
So my problem is that every workbook has a different worksheet name for the data. My macro has to work for all WBs.
I've tried the Indirect function, and this configuration only works for one row of data and populates the rest of the column with the same number(note that $A$2 is the location with the Worksheet name that contains the data):
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN(Indirect($A$2&""!B58:IQ58"")"
I have tried so many different configs and can't figure this one out. I'll get a name or a ref error...it's driving me nuts, hoping someone here can help me!
Thanks in advance:-)
You can use a cell reference's value just as easily as a string literal.
(Sense of deja-vu there - I wrote that as part of an answer just this morning!)
So, instead of hard-coding "Data" into the formula
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN(Data!B58:IQ58)"
you can insert the value from a cell
Worksheets("Stats").Range("B14:B1868").Formula = "=MIN('" & Worksheets("Stats").Range("A2").Value & "'!B58:IQ58)"
(I added apostrophes around the sheet name as well, just in case it contained spaces or other special characters.)
First time I'm writing a stackOverflow question so please let me know if I do anything wrong.
I've searched for a few hours now and haven't been able to find a solution to my problem (usually I find the answer hence why this is my first question after using stackoverflow for a few years as a lurker).
Basically I'm trying to write a modified VLOOKUP function that functions similar to VLOOKUP except it return the "next smallest larger" value instead of the default "previous largest smaller" value. I'm aware of the index/match method unfortunately I would need to carefully replace literally thousands of VLOOKUPs manually that already exist in the workbook I'm currently cleaning up at work. Therefore I resorted to writing a VLOOKUPnew function so I could just "find/replace" all the VLOOKUP with VLOOKUPnew.
Function VLOOKUPnew(lookup_value As Variant, table_array As Range, _
col_index_num As Integer, Optional exactMatch As Boolean) As Variant
Dim row As Integer
Debug.Print table_array.Address
With Application
On Error GoTo NO_ROW
row = .Match(lookup_value, table_array.Columns(1), 0)
On Error GoTo 0
If row = -1 And Not exactMatch Then
row = .Match(lookup_value, table_array.Columns(1), 1)
row = row + 1
End If
VLOOKUPnew = .index(table_array.Columns(col_index_num), row, 0)
End With
Exit Function
NO_ROW:
row = -1
Resume Next
End Function
And I succeeding in writing the function but hit a snag. Because I declared "table_array" as a Range, vba fails to identify range references to other workbooks
e.g. "=VLOOKUPnew($A432,'reallyLongFilepath/[filename.xlsx]tablename'!$B$6:$N$35,columnNumber,0),FALSE)" resolves to a #VALUE error
The really weird thing is that if I open the file, then the filepath drops out of the formula (becoming just "=VLOOKUPnew($A432,'[filename.xlsx]tablename'!$B$6:$N$35,columnNumber,0),FALSE)") and then my custom function works just fine returning the correct value.
So my problem is how do I resolve not having to open the other file to use this workbook. I'm not even sure how Excel is passing the address or range to the custom formula so I'm suspecting it's breaking when the filepath is included in the range reference. Is there a way to split the filepath, filename, sheet and address (after it has been passed in)? Or possibly pass it in as a string then easily split it? Or pass it in as something that will correctly identify the range in the different workbook?
Keep in mind that I'm trying to avoid changing the arguments of the function because I want to do the find/replace trick and that this is for work so there's a restraint on too much change in data layout. Also that the workbook is for other employees to use and I'm just setting it up for use.
Thanks in advance!
Andrew
You face quite a dilemma here!
The root problem is that while VLOOKUP can look into closed workbooks, a Range parameter in a UDF cannot. The range reference resolves to an error, so the function call fails with a type mismatch. If you change the table_array parameter type to Variant and put a break on the function header, you will see the parameter value as Error 2036.
While there are ways to look into closed workbooks, all of them (AFAIK) are quite slow. Since you mention ... I would need to carefully replace literally thousands of VLOOKUPs ... I suspect any solution along these lines would be unacceptably slow.
My reccomendation would be to go the INDEX/MATCH route, and write a VBA macro to do the formula updates for you.