VBA_Offset for Column with Header - excel

I've a data where I applied filter to the column based on its header. Now I want to make some changes to filtered data i.e. visible cells in the same column. I've below mentioned code where filter has been applied to column with header "Query Type" & it's column letter is "E". Is it possible to put offset based on the column header instead of column letter? Because column gets changing everytime. In below example, how E2 or E can be replaced dynamically to accommodate column with header? I tried replacing "E" with FiltCol; however it is not working.
Sub Filter()
Dim FiltCol As Variant
FiltCol = Rows("1:1").Find(What:="Query Type", LookAt:=xlWhole).Column
ActiveSheet.UsedRange.AutoFilter Field:=FiltCol, Criteria1:="Rejected"
ActiveSheet.Range("E2", Range("E" & Rows.Count).End(xlUp)).SpecialCells(xlCellTypeVisible).FormulaR1C1 = "Accepted"
End Sub

When you want to deal with column numbers, you can use the .Cells-property of the worksheet. Cells expects 2 parameters, row and column. The row is always a (long) number, the column can be specified as number or with the column character(s)
The following terms are all the same:
ActiveSheet.Range("D3")
ActiveSheet.Cells(3, 4)
ActiveSheet.Cells(3, "D")
Your code could look like
Sub Filter()
Dim FiltCol As Variant
With ActiveSheet
FiltCol = .Rows("1:1").Find(What:="Query Type", LookAt:=xlWhole).Column
Dim lastRow As Long
lastRow = .Cells(.Rows.Count, FiltCol).End(xlUp).row
.UsedRange.AutoFilter Field:=FiltCol, Criteria1:="Rejected"
Dim visibleCells As Range
On Error Resume Next ' Avoid runtime error if nothing is found
Set visibleCells = .Range(.Cells(2, FiltCol), .Cells(lastRow, FiltCol)).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not visibleCells Is Nothing Then
visibleCells.Value2 = "Accepted"
End If
End With
End Sub

cells(1,1).offset(2,3)
will get you from A1 to D3
lastRow = Cells(105000, FiltCol).End(xlUp).Row <<< This is poor, see edit below
ActiveSheet.Range(Cells(2, FiltCol).Offset(0, 1), Cells(lastRow, FiltCol).Offset(0, 1)).SpecialCells(xlCellTypeVisible).FormulaR1C1 = "Accepted"
edit:
Better to dynamically describe the last row
lastRow = Cells(Cells.Rows.Count, FiltCol).End(xlUp).Row

Related

Compare 2 columns and highlight new items in second column

Situation:
I have an excel macro which imports a column of data into an existing sheet in the next free column, it also clears formatting of the previous column which will be set at the completion of the macro (the bit I'm trying to work out here).
I need the code to compare each cell in the new column to the previous column to see if there is a match, if no match then highlight the cell.
Problem:
I keep getting type mismatch errors, or the code does not run. My latest attempt (which incluces copying the unique data to a different sheet which would be a bonus if working) is below.
Dim rngCell As Range
For Each rngCell In Range(Cells(2, Worksheets("Data").Columns(LastColumn)), Cells(10000, Worksheets("Data").Columns(LastColumn)))
'I know hardcoding values is bad, but i did this for testing purposes
If WorksheetFunction.CountIf(Range(Cells(2, Worksheets("Data").Columns(LastColumn)), Cells(10000, Worksheets("Data").Columns(LastColumn))), rngCell) = 0 Then
Range("D" & Rows.Count).End(xlUp).Offset(1) = rngCell
End If
Next
Ideal outcome:
The code runs through and highlights unique values in column 1 (using column index references as each day a new column is imported).
The code copies the unique values to another sheet for ease of use.
Thanks in advance for any pointers or code.
Since you are only posting a part of your code, I'd have to assume that the part wher you insert and clear formatting is already working well.
Sometimes you define a worksheet, sometimes you don't so I'm not sure what sheet we are working with here.
But just to clarify, if you had data in column "A", and then ran the macro to paste data in column "B", would you want something like this:
prevCol = 1
lastCol = 2
Set Rng = Range(Cells(2, prevCol), Cells(100, prevCol))
Set Rng2 = Range(Cells(2, lastCol), Cells(100, lastCol))
For Each rngCell In Rng
If WorksheetFunction.CountIf(Rng2, rngCell) = 0 Then
rngCell.Interior.Color = RGB(250, 230, 20)
End If
Next
Where unique items in the column that existed before the new one got inserted are highligted?
So in the end, maybe something more like this:
Sub SubName()
Dim sht As Worksheet
Dim lastCol As Long, lastRow As Long
Set sht = Worksheets("Data") 'or whatever
' Instert column
' clear formatting
lastCol = sht.Cells(2, sht.Columns.Count).End(xlToLeft).Column
lastRow = sht.Cells(sht.Rows.Count, lastCol - 1).End(xlUp).Row
For Each rngCell In Range(Cells(2, lastCol - 1), Cells(lastRow, lastCol - 1))
If WorksheetFunction.CountIf(Range(Cells(2, lastCol), Cells(lastRow, lastCol)), rngCell) = 0 Then
rngCell.Interior.Color = RGB(250, 230, 20)
End If
Next
End Sub

Concat several columns using column names in Excel VBA

As shown in image_1, I have the raw data of a product as shown in Column B to Column F. I want to add column A, which concats the "Model", "Year", "Number" data into a string. I know I can achieve this simply by
[a2] = "=concat(B2,D2,F2)", and then filldown. But the problem is that the raw file I receive every day is inconsistent in terms of the order of the columns. Therefore, I couldn't use a static line of code displayed above.
I can probably use a combination of for loop and if/else to test if the column name equal to "Model", "Year", "Number", and if yes, grab its column number...
However, I'm wondering if there's a more direct and elegant way of achieving this. Any thoughts?
A simple approach based on sorting columns by header and merging data in columns with constant numbers (assuming constant number of columns but different order). If the number of columns is variable, this code will not work.
Sub concat()
Dim rng As Range
With ThisWorkbook.Worksheets(1)
Set rng = .Range("A1").CurrentRegion
' columns sort
With .Sort
.SortFields.Clear
.SortFields.Add2 Key:=rng.Rows(1)
.SetRange rng
.Orientation = xlLeftToRight
.Apply
End With
Set rng = rng.Columns(1)
rng.Insert ' add cells at left for "Concat"
Set rng = rng.Offset(0, -1)
rng(1) = "Concat" ' add header
Intersect(rng, rng.Offset(1)).FormulaR1C1 = "=CONCAT(RC[2],RC[5],RC[3])"
End With
End Sub
Before
After
If you want to add the "Concat" column and formula without reordering the columns, you can do that with vba like this
Sub Demo()
Dim ws As Worksheet
Dim colModel As Variant
Dim colYear As Variant
Dim colNum As Variant
Dim LastRow As Long
Set ws = ActiveSheet ' or any means you choose
LastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If ws.Cells(1, 1) <> "Concat" Then
'Insert new column
ws.Columns(1).Insert
' New column header
ws.Cells(1, 1) = "Concat"
End If
' get colum positions
colModel = Application.Match("Model", ws.Rows(1), 0)
colYear = Application.Match("Year", ws.Rows(1), 0)
colNum = Application.Match("Number", ws.Rows(1), 0)
' Check if columns exist
If IsError(colModel) Then
MsgBox "Column ""Model"" not found", vbCritical + vbOKOnly, "Error"
Exit Sub
End If
If IsError(colYear) Then
MsgBox "Column ""Year"" not found", vbCritical + vbOKOnly, "Error"
Exit Sub
End If
If IsError(colNum) Then
MsgBox "Column ""Number"" not found", vbCritical + vbOKOnly, "Error"
Exit Sub
End If
' Insert Formula
ws.Range(ws.Cells(2, 1), ws.Cells(LastRow, 1)).FormulaR1C1 = "=RC[" & colModel - 1 & "]&RC[" & colYear - 1 & "]&RC[" & colNum - 1 & "]"
End Sub
Alternatively, you could also use a formula in column A to find the column positions
In Excel365
=XLOOKUP("Model",$1:$1,2:2,,0)&XLOOKUP("Year",$1:$1,2:2,,0)&XLOOKUP("Number",$1:$1,2:2,,0)
For pre 365
=INDEX(2:2,MATCH("Model",$1:$1,0))&INDEX(2:2,MATCH("Year",$1:$1,0))&INDEX(2:2,MATCH("Number",$1:$1,0))
If they are always the same columns, just the order changes then sort by column headings first before concatenating, that way they will always be in the same position.
If you have differing columns and the ones you are interested in are somewhere within it, then you could use the following formula:
=HLOOKUP("Heading_Name","Data_Range",Row_No,FALSE) to extract each of the columns you are interested in. Concatenating the results of these would give you what you want and will work for any arrangement of columns and sizes of data providing you declare the range properly.

Using variable as column reference to autosum said column VBA

I'm really new to VBA and have been working section by section on a number of pieces of code to format a worksheet (I've been doing it piece by piece so that I understand how each works, and using a final macro to Call all the macros into one long process).
Issue is sometimes the worksheets I work with are not exported with columns in the same order from month to month (out of my control), thus to autosum a particular column I have to Find the column header, then autosum that column, but this makes the column letter(or number) completely variable. I know how to work with rows as variables, but I'm stuck on column. I've been scouring forums to try and find a concise explanation, but to no avail, yet.
This code DOES work for column Y specifically, but I'm trying to figure out how to get it to use a variable for the column.
For example, I'm using a separate Macro called "FindInvoiceColumn" to select the 1st cell in the column that contains the string "invoice_amount", then I'd like to use something like I wrote below to set "ColumnAddress" as the column value of that cell. As far as I know .Column returns the column number, which is fine, but I'm assuming I'd have to use with Cells() instead of Range(), I just don't know how to get here.
(Part of the code also shows Adding the word "Total" to the left of the cell containing the autosum value, and making both bold).
Here's what I have so far:
Dim Rng As Range
Dim c As Range
Set Rng = Range("Y" & rows.Count).End(xlUp).Offset(1, 0)
Set c = Range("Y1").End(xlDown).Offset(1, 0)
c.Formula = "=SUM(" & Rng.Address(False, False) & ")"
'Selects next empty row of column X to add "Total" label for sum of column Y'
Range("X" & Cells.rows.Count).End(xlUp).Offset(1, 0).Select
ActiveCell.FormulaR1C1 = "Total"
'Bolds Total and the Sum of invoices'
Range("X" & Cells.rows.Count).End(xlUp).Select
Selection.Font.Bold = True
Range("Y" & Cells.rows.Count).End(xlUp).Select
Selection.Font.Bold = True```
'The below is what I'd like to use to find the dynamic value of the column.'
'Finds cell in row 1 that contains column header "invoice_amount" and selects it'
Call FindInvoiceColumn
'Dim ColumnAddress As Integer
ColumnAddress = ActiveCell.Column
You can use .Address to get a column reference, such that:
Sub test()
Dim varCol As String
varCol = Columns(ActiveCell.Column).Address
Debug.Print varCol 'OUTPUTS $A:$A when I had cells(1,1) selected
End Sub
In the above example, I chose a single cell to A) find it's column reference, via .Column, and B) found the .address of said column.
You could also perform the sum on a defined range using .cells() notation, rather than .range() notation.
Sub test2()
Dim rng As Range
Set rng = Range(Cells(1, 1), Cells(2, 1))
Cells(3, 1).Formula = "=sum(" & rng.Address & ")"
End Sub
The above code ouputs:
Specific to using the .cells() notation, you can make your column reference a variable, e.g.:
dim r as long, c as long
r = 1
c = 4
debug.print cells(r,c).address `should output $D$1 (untested)
You can choose r or c to fit your needs.
And as always... avoid select/activate where possible!!!
Edit
Adding use of last row via code since comments are terrible:
dim col as long
col = 25 'Y
With sheets("name")
dim lastRow as long
lastRow = .cells(.rows.count,col).end(xlup).row
Dim rng As Range
Set rng = .Range(.Cells(1, 1), .Cells(lastRow, col))
end with
This is exactly why I mentioned the specifics abotu the notation after that section (use of r and c as variables).
I've used this code to set a column number if your header is in a variable position
Dim F As Object
ColumnAddress = 0
With ActiveSheet.Rows(1)
Set F = .Find(What:="invoice_amount", LookAt:=xlWhole)
If F Is Nothing Then
MsgBox "This is not a proper file"
' other code
Else
ColumnAddress = F.Column
End If
End With
You would then use Cells() in place of range to do further work with the result of ColumnAddress. Also, ColumnAddress should dim as Long, to be accurate.

Code for automatically entering date/time stamp into cell based upon table heading

I have a excel vba workflow to enter data into a table from a data input sheet (inputwks). This would be as follows:
Press button, adds a new row to table.
Enters time stamp into specific column header name "Date/Time" column. (column M in the code below)
Enters input data from inputwks into specific table header name "data record" column. (shown as column 3 in the code below)
I don't want to use column letters or numbers because I may change the order (e.g. date/time may look better to the user in column A instead of M.).
How do I change the code below so the macro will search for "date/Time" columns instead of going to column M and the same for "data record"?
Thank you for any help given.
Sub UpdateLogWorksheet()
'Dim historyWks As Worksheet
Dim ccarWks As Worksheet
Dim inputWks As Worksheet
Dim nextRow As Long
Dim oCol As Long
'======================Named range setup=========================
Dim myCopy As Range
Dim myTest As Range
Dim lRsp As Long
'=====================worksheet name setup=======================
Set inputWks = Worksheets("Input (Modded)")
Set ccarWks = Worksheets("CCAR - VB Template Ver2.0")
oCol = 3
'===================Copy/paste data commands==================
Set myCopy = inputWks.Range("inputvalue")
With ccarWks
nextRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
End With
With ccarWks
With .Cells(nextRow, "M") 'was "A"
.Value = Now
.NumberFormat = "dd/mm/yyyy hh:mm:ss" 'enter date and time stamp in record
End With
myCopy.Copy
.Cells(nextRow, 3).PasteSpecial Paste:=xlPasteValues, Transpose:=True
Application.CutCopyMode = False
End With
End Sub
I've had to deal with this in the past too, where you can't be certain that the field with be in the same column because the source table has changed.
If you would always have the same field header (column header) then you can use the code I've used before.
Public Function FindColumn(ws As Worksheet, name As String, Optional headerRow As Integer = 1) As Integer
On Error GoTo FUNC_ERR
Dim sRng As Range: Set sRng = ws.Rows(headerRow).Find(what:=name, _
LookIn:=xlValues, _
lookat:=xlWhole)
If sRng Is Nothing Then
'Handle if not found
Else
FindColumn = sRng.Column
End If
FUNC_EXIT:
Exit Function
FUNC_ERR:
'Handle error
End Function
HeaderRow is optional and assumed to be row 1, but you can also call out a different row if necessary.
Then instead of using the column letter in the .cell() callout, you can use the returned column number.
dim columnNumber as Integer: columnNumber = FindColumn(ccarWks, "Header Name")
With ccarWks
With .Cells(nextRow, columnNumber)
.Value = Now
.NumberFormat = "dd/mm/yyyy hh:mm:ss"
End With
'...more code...
End With
Hope this helps you.
I added this code to ensure it was able to dynamically change the column number it was looking for:
Datetimecol = Application.WorksheetFunction.Match("date/time", CCARWKS.Range("A4:AZ4"), 0)
With .Cells(nextRow, datetimecol)
This now changes the column value automatically and prevents breaking the code.

Find last row in range

I'm having a little trouble with finding the last row.
What I am trying to do is find the last row in column "A", then use that to find the last row within a range.
Example of Data:
1) LR_wbSelect = wbshtSelect.cells(Rows.count, "A").End(xlUp).Row - 22
2) LR_wbSelectNew = wbshtSelect.cells(LR_wbSelect, "A").End(xlUp).Row
I am using the last row in column "A" as the data from row 29 down will always be the same length, the rows used in column "B" from row 29 can be a varying number of rows.
So I am trying to use LR_wbSelect in column "A" to get my starting last Row, then within LR_wbSelectNew using it as the starting point to look up from.
This works when the column I set to "A", LR_wbSelectNew gives me the row of "17", but when I change the column in LR_wbSelectNew to "B" it doesn't give the correct last row of "18".
I can change the column to "C, D, E, F" and the code works fine, but the only column that I can use is "B" because it will always have data in it, where the rest of that row could have a blank cell.
After doing some testing on the sheet, by pressing CRTL & Up from the lastring point of LR_wbSelect column "B" ignores the data in the rows and go to the row where it find data. I can't see a reason why Excel doesn't think there is data in these cells?
There are mulitple results and methods when searching for the LastRow (in Column B).
When using Cells(.Rows.Count, "B").End(xlUp).Row you will get the last row with data in Column B (it ignores rows with spaces, and goes all the way down).
When using:
With wbshtSelect.Range("B10").CurrentRegion
LR_wbSelectNew = .Rows(.Rows.Count).Row
End With
You are searching for the last row with data in Column B of the CurrentRegion, that starts from cell B10, untill the first line without data (it stops on the first row with empty row).
Full Code:
Sub GetLastRow()
Dim wbshtSelect As Worksheet
Dim LR_wbSelectNew As Long
' modify "Sheet2" to your sheet's name
Set wbshtSelect = Sheets("Sheet2")
' find last row with data in Column B
With wbshtSelect
LR_wbSelectNew = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
' for debug only
Debug.Print LR_wbSelectNew ' >>result 31
' find last row with data in Column B at current regioun starting at cell B10
With wbshtSelect.Range("B10").CurrentRegion
LR_wbSelectNew = .Rows(.Rows.Count).Row
End With
' for debug only
Debug.Print LR_wbSelectNew ' >> result 18
End Sub
Edit1: code searches for last row for cells with values (it ignores blank cells with formulas inside).
Sub GetLastRow()
Dim wbshtSelect As Worksheet
Dim LR_wbSelectNew As Long
' modify "Sheet2" to your sheet's name
Set wbshtSelect = Sheets("Sheet2")
' find last row with data in Column B at current regioun starting at cell B10
With wbshtSelect.Range("B10").CurrentRegion
LR_wbSelectNew = .Rows(.Rows.Count).Row
End With
Dim Rng As Range
Set Rng = wbshtSelect.Range("B10:B" & LR_wbSelectNew)
' find last row inside the range, ignore values inside formulas
LR_wbSelectNew = Rng.Find(What:="*", _
After:=Range("B10"), _
LookAt:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
' for debug
Debug.Print LR_wbSelectNew ' << result 18 (with formulas in the range)
End Sub
Hope this piece of code helps !
Sub LastRowInOneColumn()
'Find the last used row in a Column: column A in this example
Dim LastRow As Long
With ActiveSheet
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
MsgBox LastRow
End Sub
I came here looking for a way to find the last row in a non-contiguous range. Most responses here only check one column at a time so I created a few different functions to solve this problem. I will admit, though, that my .Find() implementation is essentially the same as Shai Rado's answer.
Implementation 1 - Uses Range().Find() in reverse order
Function LastRowInRange_Find(ByVal rng As Range) As Long
'searches range from bottom up stopping when it finds anything (*)
Dim rngFind As Range
Set rngFind = rng.Find( What:="*", _
After:=rng.Parent.Cells(rng.row, rng.Column), _
LookAt:=xlWhole, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious)
If Not rngFind Is Nothing Then
LastRowInRange_Find = rngFind.row
Else
LastRowInRange_Find = rng.row
End If
End Function
Implementation 2 - Uses Range().End(xlUp) on each column
Function LastRowInRange_xlUp(ByVal rng As Range) As Long
Dim lastRowCurrent As Long
Dim lastRowBest As Long
'loop through columns in range
Dim i As Long
For i = rng.Column To rng.Column + rng.Columns.count - 1
If rng.Rows.count < Rows.count Then
lastRowCurrent = Cells(rng.row + rng.Rows.count, i).End(xlUp).row
Else
lastRowCurrent = Cells(rng.Rows.count, i).End(xlUp).row
End If
If lastRowCurrent > lastRowBest Then
lastRowBest = lastRowCurrent
End If
Next i
If lastRowBest < rng.row Then
LastRowInRange_xlUp = rng.row
Else
LastRowInRange_xlUp = lastRowBest
End If
End Function
Implementation 3 - Loops through an Array in reverse order
Function LastRowInRange_Array(ByVal rng As Range) As Long
'store range's data as an array
Dim rngValues As Variant
rngValues = rng.Value2
Dim lastRow As Long
Dim i As Long
Dim j As Long
'loop through range from left to right and from bottom upwards
For i = LBound(rngValues, 2) To UBound(rngValues, 2) 'columns
For j = UBound(rngValues, 1) To LBound(rngValues, 1) Step -1 'rows
'if cell is not empty
If Len(Trim(rngValues(j, i))) > 0 Then
If j > lastRow Then lastRow = j
Exit For
End If
Next j
Next i
If lastRow = 0 Then
LastRowInRange_Array = rng.row
Else
LastRowInRange_Array = lastRow + rng.row - 1
End If
End Function
I have not tested which of these implementations works fastest on large sets of data, but I would imagine that the winner would be _Array since it is not looping through each cell on the sheet individually but instead loops through the data stored in memory. However, I have included all 3 for variety :)
How to use
To use these functions, you drop them into your code sheet/module, specify a range as their parameter, and then they will return the "lowest" filled row within that range.
Here's how you can use any of them to solve the initial problem that was asked:
Sub answer()
Dim testRange As Range
Set testRange = Range("A1:F28")
MsgBox LastRowInRange_Find(testRange)
MsgBox LastRowInRange_xlUp(testRange)
MsgBox LastRowInRange_Array(testRange)
End Sub
Each of these will return 18.
If your wbshtSelect is defined as worksheet and you have used set to define the specific worksheet, you can use this.
Dim LastRow As Long
wbshtSelect.UsedRange ' Refresh UsedRange
LastRow = wbshtSelect.UsedRange.Rows(wbshtSelect.UsedRange.Rows.Count).Row
Otherwise take a look here http://www.ozgrid.com/VBA/ExcelRanges.htm
LR_wbSelectNew = wbshtSelect.cells(LR_wbSelect, "B").End(xlUp).Row
Why are you using "LR_wbSelect" as the row counter? If you want to know the last row of column 'B', you should use Rows.count
Rows.count --> Returns maximum number of rows (which is 1048576 for Excel 2007 and up)
End(xlUp) --> Moves the pointer upward to the last used row
So,
cells(Rows.count, "A").End(xlUp).Row --> This moves the pointer to the last row if the column 'A' (as if you are pressing Crtl+Up keys when A1048576 cell is selected)
So, use Rows.count to select the last row for column 'B' as well. If you have some specific requirement related to LR_wbSelect, please mention it.
Alternatively, if you want to know the last row used in a sheet, you may use the below:
mySheet.Cells.SpecialCells(xlCellTypeLastCell).Row
LR_wbSelect = ThisWorkbook.Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
Simple function that return last row no. in specific sheet.
It takes the last address in UsedRange and retrieve last row number.
Feel to free change the code and use standard range insead of UsedRange.
Function FindLastRow(wsToCheck As Worksheet) As Long
Dim str As String
str = wsToCheck.UsedRange.AddressLocal()
FindLastRow = Right(str, InStr(1, StrReverse(str), "$") - 1)
End Function
Range().End will bring you to the end of a code block. If the starting cell is empty, it brings you the the first used cell or the last cell. It the cells is not empty it brings you to the last used cell. For this reason, you need to test whether or not the cell in column B is to determine whether to use LR_wbSelectNew as the last row.
With wbshtSelect
LR_wbSelect = .Cells(Rows.Count, "A").End(xlUp).Row - 22
If .Cells(LR_wbSelect, "B") <> "" Then
LR_wbSelectNew = LR_wbSelect
Else
LR_wbSelectNew = .Cells(LR_wbSelect, "B").End(xlUp).Row
End If
End With
This code defines a Target range that extends from A1 to the last row in column a - 22 and extends 10 columns.
Dim Target As Range
With wbshtSelect
Set Target = .Range("A1", .Cells(Rows.Count, "A").End(xlUp).Offset(-22)).Resize(, 10)
End With
'This is sure method to find or catch last row in any column even 'if some cell are blank in-between. (Excel-2007)`
'This works even if sheet is not active
'mycol is the column you want to get last row number
for n=1048575 to 1 step -1
myval=cells(n,mycol)
if myval<>"" then
mylastrow=n 'this is last row in the column
exit for
end if
next
ret=msgbox("Last row in column-" & mycol & "is=" & mylastrow)
Dim rng As Range
Dim FirstRow, LastRow As long
Set rng = Selection
With rng
FirstRow = ActiveCell.Row
LastRow = .Rows(.Rows.Count).Row
End With
Shai Rado's first solution is a great one, but for some it might need a bit more elaboration:
Dim rngCurr, lastRow
rngCurr = wbshtSelect.Range("B10").CurrentRegion
lastRow = rngCurr.Rows(rngCurr.Rows.Count).Row
If you want to know the last used row in the entire worksheet:
Dim rngCurr, lastRow
rngCurr = Range("A1").CurrentRegion
lastRow = rngCurr.Rows(rngCurr.Rows.Count).Row
Backing off from the range to the worksheet will get you the whole sheet extents of the range used on the sheet (which may be smaller than you expect if the sheet doesn't have data in the top rows; but it does include internal blanks)
TheRange.Worksheet.UsedRange.Rows.Count
If there is no data in the top rows, the following will get you the first row which you need to add to the above to get the highest row number
TheRange.End(xlDown).Row
So
Dim TheRange as Range
Dim MaxRow as Long
MaxRow = TheRange.Worksheet.UsedRange.Rows.Count + TheRange.End(xlDown).Row
Will get the highest row number with data (but not the whole sheet)
Before getting into complex coding why not build something on the below principle:
MaxRow = Application.Evaluate("MIN(ROW(A10:C29)) + ROWS(A10:C29) - 1")

Resources