Get table range in VBA (dynamic) - excel

I have a program in VBA that I am working on that filters values from a table. I'm trying to make this a generic program that works with all tables that you give it. In my program I have to set the range of the table that it is filtering: Set rng = dataSheet.Range("A1:F78"). I was wondering if there was a way to get the Range of a table in excel that has some sort of text value in it so I don't have to specify it in every macro. I guess this is a type of dynamic range.

If there is one cell of your range which is always within your table, like A1 would be always top-left corner of the table. And if there is continuous range of cells until the end of your table you could use .CurrentRegion property in this way:
Set rng = dataSheet.Range("A1").CurrentRegion

Related

Setting a range to another range's visible cells only

Using VBA, I'm trying to set the values of a new range to those or an existing range. However the existing range has some hidden columns. Assigning the range by selecting visible cells only results in the new range's values containing the first value from the source range just repeated.
VBA extract:
Range("E1:F1").Value = Range("A1:C1").SpecialCells(xlCellTypeVisible).Value
Does anyone know how to resolve this?
Example Data with results:

excel countif entire row in a table

I have a structured table in excel 2016.
I want to have a cell to count the number of cells across the entire row within the table if it matches my criteria.
I have tried putting this formula in column A on each row =COUNTIF(Table[#],"my criteria") but that does not count properly.
However, this works: =COUNTIF(Table[#[ColB]:[ColH]],"my criteria"). But since my table will expand, I don't want to specify the column name [ColB], I want to refer to the entre row in the table.
A header
countif
colC
colD
colE
First
formula
A
C
Second
formula
B
C
formula = =COUNTIF(Table[#],"A") does not work
formula = =COUNTIF(Table[#[colC]:[colE]],"A") works
My table will expand both horizontally and vertically.
Please Note: This solution is only available if you chose to use VBA. VBA does not work in web versions of Excel. Please ensure that you resave your workbook as a macro-enabled workbook before proceeding.
You can choose to use VBA and create your own custom worksheet formula. Since this question didn't start out as a VBA issue, I will be a bit more detailed on the process of setting this up as opposed to just throwing you some code and you having to figure out what to do with it.
After you've re-saved your workbook as a macro-enabled workbook, open the VBA Editor (VBE) by pressing Alt and F11 simultaneously.
In the VBE, click Insert > Module. You should now see Module1 highlighted on the left side bar (The project Explorer).
Copy and paste the following code in the blank area of the module:
Option Explicit
Public Function GetMyRowCount(Criteria As Variant) As Long
Dim ws As Worksheet
Dim tblRng As Range, RowRng As Range
With Application.Caller
Set ws = .Worksheet
Set tblRng = ws.Range(.ListObject.Name)
Set RowRng = ws.Range(ws.Cells(.Row, .Column + 1), ws.Cells(.Row, tblRng.Columns.Count))
End With
GetMyRowCount = Application.WorksheetFunction.CountIf(RowRng, Criteria)
End Function
Now use this UDF (User Designed Function) in your worksheet. In the column you would like the calculation to be in, simply type =GetMyRowCount("My Criteria") and it should calculate.
To point out how this code works in more detail:
Application.Caller is referring to the cell that this function is located in. Because we now know the location of the cell, VBA can use it's location to obtain the row data from it (which is why you don't need an argument for the row #).
RowRng is getting the starting point of the column within the ws.Range(...) function with the first ws.Cells(...) function. .Row is the row # from the GetMyRowCount function (using Application.Caller.Row method), and the 3 is simply the static column C.
The way we grab the last column we need is by counting the total # of columns within the table: ws.Cells(.Row, tblRng.Columns.Count)
Using the information we obtained from bullets 2 and 3, we can establish the entire range of the lookup we need, and then place this range into your CountIf() function, along with the criteria you passed with the function's argument:
GetMyRowCount = Application.WorksheetFunction.CountIf(RowRng, Criteria)
As you can see in the following example, I wanted to count the number of times in the row the number 1 occurred:
Another example showing it works with text as well by using "Apple" as the criteria:
Try this: =COUNTIF(B:B,"my citeria"), so if your Column is A, range would be A:A, for B it is B:B.
Let me know if this helps.

Is it possible to directly reference the Source Data of a Pivot Table?

I have a large table of data on Sheet1 and a pivot table on Sheet2 that is based on Sheet1. I would like to be able to jump from a selected PivotItem on Sheet2 directly to the cell on Sheet1 that the data is from. So far I just use
EID = ActiveCell.PivotTable.PivotFields(1).DataRange.Cells(ActiveCell.PivotCell.PivotRowLine.Position, 1).Text
Set rTarget = Sheet1.Columns(1).Find(EID, lookat:=xlWhole, MatchCase:=True)
Sheet1.activate
rTarget.activate
Which is a little messy because my current strategy to locate the data is the unique identifier in PivotFields(1) but I want to allow the user to select any cell along the Row. So I have to reference the cell address in PivotFields(1) in the same row and then search for it in Sheet1.
I would prefer to avoid using Cells.Find and was wondering if there was a method or property that might allow me to do something like:
set rTarget = Activecell.PivotItem.SourceDataRange
Where SourceDataRange would return the Range of the cell in Sheet1 that is supplying the data for the pivotitem. PivotTable.SourceData returns the entire range. I am looking for the range of an individual pivotitem.
Does such a method exist?

A "CodeName" for an Excel table column for use in VBA

I'd like to be able to retrieve values from an Excel table by row number and column name (for code readability and robustness).
In formulas, I can use Structured References with column header text and get a value from the table like this:
=INDIRECT(ADDRESS(<absolute_line_number>;COLUMN(<table_name>[<column_name>])))
This is robust for formulas because if the user renames the column, all Structured References to it in formulas will be automatically updated.
Not so for VBA code though.
For worksheets, it's possible to define Worksheet.CodeName for use in VBA code that will stay the same if the user renames the visible sheet name.
No such property exists for an Excel table AFAICS.
The best idea I currently have is to make table headers 1-cell Named Ranges. Then I can get a value from a table in VBA like this:
<sheet_codename>.Cells(<line_number>,Range("<range_name>").Column)
This, however, bothers me because Named Ranges are disconnected from the table. E.g. if I rearrange tables on the sheet, the ranges will remain in the old place.
Is there a better option? "Better" means specifically:
Survives renaming and/or rearranging columns in the table, moving the table at least within the sheet
Comment
A comment in each header cell of a ListObject remains there, if the column is renamed or rearranged.
If(!) you can hide all comments by Application.DisplayCommentIndicator = xlNoIndicator (all comments neither recognisable by red triangle nor visible during mouseover), this may be a workaround:
Private Sub RecognizeColumnsOfListObject()
Dim lo As ListObject
Dim lc As ListColumn
For Each lo In ActiveSheet.ListObjects
For Each lc In lo.ListColumns
Debug.Print lc.Index ' not unique, always 1, 2, 3, ...
Debug.Print lc.Name ' not unique, changeable
If Not lc.Range.Cells(1).Comment Is Nothing Then
Debug.Print lc.Range.Cells(1).Comment.text ' unique
End If
Next lc
Next lo
End Sub
Named Range
If I give each header cell of a ListObject a name, it moves with the column if I rearrange the ListObject. As its Name.Value or Name.RefersTo begins with =<ListObjectName>... I get the absolute address by this:
Dim n As Name
With <sheet_codename>
For Each n In .Names
Debug.Print .Range(Mid(n.RefersTo, 1)).Address
Next n
End With
On closer inspection, there's nothing wrong with making table headers Named Ranges. That's because such Names get assigned to a Structured Reference rather than raw cell address, so they will move around together with the column!
On the downside, this name is not printed in the address field (at least, in Office 2007) when selecting the header which is rather inconvenient ('cuz I can't quickly look up the name I should type into the code to get this column).

Referencing a named offset address within a range function in VBA Excel

I'm trying to call a range between a fixed cell and a dynamic cell (which i can reference using a defined name and an offset). Im not too sure how the syntax should go. Currently I have:
Range("B21:Range("Anchor").Offset(-1,1)")
'(i know this is incorrect syntax, but just to show you the logic)
Where B21 is the fixed cell. And "Anchor" is the Defined name which has a dynamic cell address.
For some background: I'm trying to call all cells within a column of a table, however the table has an "add rows" function, so the last cell of the column is always changing. My "Anchor" is below the position the last row will be.
Im not very good with VBA language, so please bear with me.
You can do it like this:
Dim ws as worksheet, rng as range
set ws = activesheet
With ws
Set rng = .Range(.Range("B21"), .Range("Anchor").Offset(-1,1))
end with

Resources