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
Related
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 in my userform listbox which display 10 columns. All rows have an individual ListIndex.
I would like to display f.e. in Msgbox value after select some row and click button "Show".
But this value is not avalible in listbox. So, I thought that all rows have individual ListIndex, I can use VLOOKUP for finding my target.
For example: I selected row no. 1 then ListIndex is 1 and this is my Look up Value. In my database sheet I have the same individual ID values.
Then of course, I have to declarate range, number of column and parameter "False".
Theoretically, I expect the result after that but it doesn't work.
My code:
Dim indexno As Long 'this is my delcaration for finding Look up Value
Dim myVLookupResult As Long 'this is my declaration for VLookup Result
indexno = ListBoxResult.ListIndex 'my Lookup Value is dynamic and depend of selected row
myVLookupResult = Application.VLookup(indexno, Worksheets("DataBase").Range("A1:J100"), 5, False)
MsgBox myVLookupResult 'should display result of VLOOKUP
But the result is error: Run-Time error: 13 - Type mismatch.
I guess the problem is with convert type of Dim from int to string.
Someone could support me, please? Thanks in advance!
The lookup value of a VLOOKUP worksheet function must be of the same data type as the data in which it is to be found. In your code the lookup value is of Long data type. If the column in which you are looking for it has text, the number you are looking for will not be found. So, you change the lookup value to Variant and hope that Excel will be able to work out what you want. But the better way is to examine your data column and look up the type of value you actually have there.
Next, given you are looking for a number which you have assigned to a Variant and Excel, in consequence, managed to find the string-equivalent of that number, that would be the functions return value, a text string. In most cases Excel is quite generous in this sort of situations but if it does complain about "Data Type" then it's because you are trying to assign a text string to a variable of Long data type.
Other than that, as #Michal Palko already pointed out, the ListBox's ListIndex is 0-based. If the numbers in your worksheet are 1-based the return of VLOOKUP won't be "totally different" but it will be from the adjacent row and therefore unexpected.
But I want to alert you to another possibility. As you know, you can load many columns into your list box. You can show some of the columns and hide others but you can access them all with code like this:-
With ListBox1
Debug.Print .List(.ListIndex, 3)
End With
This snippet will print out the value of the 3rd column in the row of ListIndex. You might also assign this value to a variable and, perhaps, have no need for VLOOKUP.
I'm trying to develop an Excel application that asks our 4D database for information. To do that, I built a query builder and it works. Now I want to make it more generic so that when I call the query builder, I can pass it a range in which the tables and fields the query is based on are stored. Here is a line where I call the sub and pass it the parameters:
QueryDatabase Worksheets("TablesAndFields").Range("A2:R20"), Worksheets("TablesAndFields"), Worksheets("Import")
Here is the first line in the sub:
Sub QueryDatabase(QuerySpecs As Range, QuerySheet As Worksheet, TargetSheet As Worksheet)
One of the things I need to do is have the VBA figure out the row of the last fields in the various columns. Here is my current code:
LastRowReportTables = QuerySpecs.Offset(0, 3).End(xlDown).Row
LastRowQuery = QuerySpecs.Offset(0, 6).End(xlDown).Row
LastRowSort = QuerySpecs.Offset(0, 14).End(xlDown).Row
This returns the same value for all 3 of them (the second line of the range). It seems to do this regardless of which cells have values in them. For instance, in the case of the range specified above it will return 3. If the range is "A22:R40" it returns 23. What I really need is for it to return the row relative to it's position in the range, but I could fake that if necessary by subtracting the largest multiple of 20 less than the result. (I'm formatting my query builder to fit in 19 rows + a buffer row.) So far, I haven't even been able to get it to return different results for the LastRow variables.
In addition to the Offset method you see above, I've also tried putting it in a With QuerySpecs... End With block. I don't remember the exact result, but I couldn't get that to work either.
The next thing I will need to do is pull values out of the various cells kind of like this:
strStartCell = QuerySpecs.Offset(0, 18).Value
This throws a Run time error 13: Type mismatch. Does anybody have any advice on how to accomplish my goals? Thank-you!
I suggest reading the documentation of the resources you are trying to use.
This can be done by selecting the word which you want to search in the documentation, then press the [F1] key, that will take you to the Microsoft documentation page (i.e. if you select Row and press [F1] will take you to the page Range.Row Property (Excel).
Reading the documentation for Row and Offset will help you understand these Range.Properties and the returns you are getting from your code.
To get the last non-empty row in a range of one column, assuming all data in the range is continuous (i.e. there are no empty cells between rows with content), use these lines:
With WorksheetFunction
LastRowReportTables = .CountA(QuerySpecs.Columns(4))
LastRowQuery = .CountA(QuerySpecs.Columns(7))
LastRowSort = .CountA(QuerySpecs.Columns(15))
End With
Note: If the "QuerySpecs" range is a kind of database, all columns in the fields should have the same number of records, if so, it should be sufficient to obtain the last row for one field only.
To obtain the value of a cell within a range, use this line:
This returns the value of the field (column) 18,row 13 of the QuerySpecs range
strStartCell = QuerySpecs.Cells(13, 18).Value2
The Run-time error 13: Type mismatch is generated when trying to assign an array to a string variable.
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)
Trying to figure out a Macro that will do the following:
1) Find a specified header (ie: "status")
2) Filter this column to a specified value (ie: "discontinued")
3) Once filtered, find another specified column (ie: "replaced with")
The challenge for me is I can't figure out how to set it so the column "Field" is variable to the sheet its on.
I have multiple files that have the "status" header in different locations, (one file has it in column c, another in column f, etc). Is there a way to have excel find that specific column in each sheet is and filter it?
Find a specified header
You would implement this quite exactly as you'd subconsciously do in your head while "finding a specific header": iterate all columns from left to right until you find it, ...and throw a fit when it's not there. The function should be generic enough to not be attached to any specific worksheet, so you should pass it a worksheet as a parameter.
Something like this:
'returns column number of specified header, or -1 if not found.
Public Function FindHeader(sheet As Worksheet, header As String) As Integer
...
End Function
Find your header row, and loop through the non-empty cells. Your exit conditions would be either you've iterated all non-empty cells in the header row, or you've found a cell that matches the content you're looking for. When the loop exits, you need to know two things: whether the header was found, and if it was, under which column.
Filter this column for a specified value
You don't say how the value gets specified, so I'll assume all you need is a Sub that takes an Integer for the header column to filter, and a String parameter for the value to filter with. Using AutoFilter -related methods should get you there. Again you want to be able to use this code for any sheet, so pass one as a parameter:
Public Sub FilterForSpecificValue(sheet As Worksheet, columnIndex As Integer, filter As String)
...
End Sub
Find another specified column
Well, just reuse the function from #1, passing it the same sheet with another column header:
result = FindHeader(ActiveSheet, "AnotherHeader")
That's about as much as I can give you for the question you've asked; feel free to ask SO for any specific programming issues you might encounter implementing this. However it's possible you won't even need to ask, because it's very likely that somebody else somewhere has documented exactly how to do it - here are a couple links that can help you:
Selecting an entire row of data (StackOverflow)
Looping Through a Range of cells (MSDN)
StringContainsAny (StackOverflow)
AutoFilter Method (MSDN)