how to know the last row filled in vba (excel)? [duplicate] - vba

This question already has answers here:
Return a range from A1 to the true last used cell
(4 answers)
Closed 7 years ago.
I have a sheet calls "Recap" and I want to know how much line that I have in this sheet.I tried with this code:
Function FindingLastRow(Mysheet As String) As Long
Dim sht As Worksheet
Dim LastRow As Long
Set sht = ThisWorkbook.Worksheets(Mysheet)
LastRow = sht.Cells(sht.Rows.Count, "A").End(xlUp).Row
FindingLastRow = LastRow
End Function
......
in my macro i tried this:
....
Dim lastR As Long
lastR=FindingLastRow("Recap")
msgBox lastR
.....

You are passing a string into your FindingLastRow function but are not using it. shiit is the parameter being passed in but you later try to use something called Mysheet.
Function FindingLastRow(Optional MySheet As String, Optional sCOL as String = "A") As Long
Dim sht As Worksheet
Dim LastRow As Long
If Not CBool(Len(MySheet)) Then MySheet = ActiveSheet.Name
Set sht = ThisWorkbook.Worksheets(MySheet)
LastRow = sht.Cells(sht.Rows.Count, sCOL).End(xlUp).Row
FindingLastRow = LastRow
Set sht = nothing
End Function
Sub test_FindingLastRow()
Dim lastR As Long
lastR = FindingLastRow
MsgBox lastR
lastR = FindingLastRow("Recap")
MsgBox lastR
lastR = FindingLastRow("Recap", "B")
MsgBox lastR
End Sub
If no worksheet name is passed in then the currently ActiveSheet's name is used. If no alphabetic column name is passed in it will use column A.

If you want to find the last cell in a book other than ThisWorkbook:
Function FindingLastRow( _
ByVal shtName As String, _
Optional ByVal colLetter As Variant, _
Optional ByRef wkBk As Variant _
) As Long
Dim colId As String
If IsMissing(colLetter) Then
colId = "A"
Else
colId = colLetter
End If
Dim myTargBk As Excel.Workbook
If IsMissing(wkBk) Then
Set myTargBk = ThisWorkbook
Else
Set myTargBk = wkBk
End If
Dim sht As Worksheet
Set sht = myTargBk.Worksheets(shtName)
With sht
FindingLastRow = .Cells(.Rows.Count, colId).End(Excel.xlUp).Row
End With
End Function
Used like this:
Sub findLast()
MsgBox FindingLastRow("Sheet1")
End Sub
Or to find the last row of column A in a different open workbook...
Sub findLast2()
Dim w As Excel.Workbook
Set w = Excel.Workbooks("Norf.xlsx")
MsgBox FindingLastRow("Sheet1", , w)
End Sub
Or to find the last row of column B in a different open workbook...
Sub findLast3()
Dim w As Excel.Workbook
Set w = Excel.Workbooks("Norf.xlsx")
MsgBox FindingLastRow("Sheet1", "B", w)
End Sub

You're also assuming that the last cell will be in column A which may not be the case, so I tend to revert to something like this:
Function FindingLastRow(sheetName As String) As Long
FindingLastRow = Sheets("sheetName").Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
End Function

The UsedRange on a Worksheet variable is very helpful here. You really don't need a UDF to get the row count.
LastRow = Worksheets("Recap").UsedRange.Rows.Count
This method only works if your data starts in row 1 and the sheet does not have formatting outside of the data. You could add in the starting row + UsedRange.Cells(1,1).Row if you know the data starts somewhere other than row 1. The second issue prevents the use of UsedRange.

Related

Copy and paste VBA code - I want to use across multiple sheets

I'm very new to VBA. I have some code that will copy data that meets a certain criteria in one sheet to another master sheet. I have multiple other worksheets that I want to copy from into the master. How do I amend my code to do that please?
Thanks in advance.
Sub copyPaste()
Dim ws As Worksheet
Dim wt As Worksheet
Set ws = Sheets("S_Q")
Set wt = Sheets("master")
Dim i As Integer
Dim lr As Integer
lr = ws.Range("y" & Rows.Count).End(xlUp).Row
Dim lt As Long
For i = 1 To lr
lt = wt.Range("y" & Rows.Count).End(xlUp).Row
If ws.Range("bz" & i) > 14 Then
ws.Range("y" & i).EntireRow.Copy wt.Range("a" & lt + 1)
End If
Next i
End Sub
Without diving too far into the specifics of your code itself - will the criteria be the same for all worksheets you're wanting to run it on? And is the layout of the data in all of those worksheets?
If so, and if your current code is doing what you need it to do for Worksheet A and we just need to expand that to also handle Worksheets B through X, then you could get rid of your dim/set ws lines, and instead change your first line to
sub copyPaste(ws as worksheet)
This would allow you to then use a separate procedure to call this procedure for each of your worksheets that it needs to be run on. Below is an example using the worksheet from your original code:
call copyPaste(ThisWorkbook.Sheets("S_Q"))
I would put the sheets of interest to loop over in an array and loop that. I would also use Union to gather the qualifying ranges and paste in one go to be more efficient.
I would also use a helper function to retrieve the last row and add one to that to get next row.
Also, use Long rather than Integer to avoid potential overflow as there are more rows in a sheet than Integer can handle.
Option Explicit
Public Sub copyPaste()
Dim ws As Worksheet, wt As Worksheet, sheetsOfInterest(), unionRng As Range
Dim i As Long, lastRow As Long, lastRowMaster As Long
Application.ScreenUpdating = False
sheetsOfInterest = Array("Sheet1", "Sheet2", "S_Q")
Set wt = ThisWorkbook.Worksheets("master")
For Each ws In ThisWorkbook.Worksheets(sheetsOfInterest)
lastRow = GetLastRow(ws, 25)
For i = 1 To lastRow
If ws.Range("BZ" & i) > 14 Then
If Not unionRng Is Nothing Then
Set unionRng = Union(unionRng, ws.Range("bz" & i))
Else
Set unionRng = ws.Range("BZ" & i)
End If
End If
Next i
If Not unionRng Is Nothing Then
With wt
unionRng.EntireRow.Copy .Range("A" & GetLastRow(wt, 1) + 1)
End With
End If
Set unionRng = Nothing
Next
Application.ScreenUpdating = True
End Sub
Public Function GetLastRow(ByVal ws As Worksheet, Optional ByVal columnNumber As Long = 1) As Long
With ws
GetLastRow = .Cells(.Rows.Count, columnNumber).End(xlUp).Row
End With
End Function
After trying the filter on various columns and it working on some and not others; with no apparent reasoning. I decided to rejig the spreadsheets and put the column to be filtered in the first column. This seems to be working so far.

vba userform , if any of the checkboxes in the frame is true then macro should not be applied on the sheetname mentioned in the checkbox

I have created a userform (to change the column and row width of active sheet or all sheets )which has three frames.
In the first frame I have given two option box. Firsts option box : - To change the row and column width from Column B onwards and other option box to change the row column width from column c onwards.
User will select anyone of them and then move to second frame: which has again two options one to make the changes in active sheet and second option box to make the changes in all the sheets.
So if the user in the first form will select first option (change row and column width from B onwards and in the second frame will select active sheet then the column and row width will change from Column B onwards in the active sheet and so on...
Now I want to create third fram which has 3 checkboxes which has name of 3 sheets (Sheet1, Sheet2 and Sheet3.) I want that when the user has selected his options in frame one and two if the user in the third fram select any of the checkboxes or all of the checkboxes then the changes should not apply in the sheetname mentioned in any of the 3 checkboxes which he has selected.
I have successfully executed frame one and frame 2 however struggling to create a code for frame 3 which will have 3 checkboxes (which contains name of 3 sheets) which is to excluded to make any row and column width changes.
Please find below my codes which are in the module:
Sub rowcolactivesheetb()
Dim exworkb As Workbook
Dim xlwksht As Worksheet
Dim lastrow1 As Long
Dim lastcolumn1 As Long
Dim firstrowDB As Long
With ActiveSheet
lastrow1 = .Cells(Rows.Count, "A").End(xlUp).Row
lastcolumn1 = .Cells(1, Columns.Count).End(xlToLeft).Column
.Range(.Cells(1, 2), .Cells(lastrow1, lastcolumn1)).Select
Selection.Cells.RowHeight = 9.14
Selection.Cells.ColumnWidth = 7.14
End With
End Sub
Sub rowcolallsheetb()
Dim exworkb As Workbook
Dim xlwksht As Worksheet
Dim lastrow1 As Long
Dim lastcolumn1 As Long
Dim firstrowDB As Long
Dim Z As Integer
Dim ShtNames() As String
ReDim ShtNames(1 To ActiveWorkbook.Sheets.Count)
For Z = 1 To Sheets.Count
ShtNames(Z) = Sheets(Z).Name
Sheets(Z).Select
lastrow1 = Sheets(Z).Cells(Rows.Count, "A").End(xlUp).Row
lastcolumn1 = Sheets(Z).Cells(1, Columns.Count).End(xlToLeft).Column
ActiveWorkbook.Sheets(Z).Range(Sheets(Z).Cells(1, 2), Sheets(Z).Cells(lastrow1, lastcolumn1)).Select
Selection.Cells.RowHeight = 9.14
Selection.Cells.ColumnWidth = 7.14
Next Z
End Sub
Sub rowcolactivesheetc()
Dim exworkb As Workbook
Dim xlwksht As Worksheet
Dim lastrow1 As Long
Dim lastcolumn1 As Long
Dim firstrowDB As Long
With ActiveSheet
lastrow1 = .Cells(Rows.Count, "A").End(xlUp).Row
lastcolumn1 = .Cells(1, Columns.Count).End(xlToLeft).Column
.Range(.Cells(1, 3), .Cells(lastrow1, lastcolumn1)).Select
Selection.Cells.RowHeight = 9.14
Selection.Cells.ColumnWidth = 7.14
End With
End Sub
Sub rowcolallsheetc()
Dim exworkb As Workbook
Dim xlwksht As Worksheet
Dim lastrow1 As Long
Dim lastcolumn1 As Long
Dim firstrowDB As Long
Dim Z As Integer
Dim ShtNames() As String
ReDim ShtNames(1 To ActiveWorkbook.Sheets.Count)
For Z = 1 To Sheets.Count
ShtNames(Z) = Sheets(Z).Name
Sheets(Z).Select
lastrow1 = Sheets(Z).Cells(Rows.Count, "A").End(xlUp).Row
lastcolumn1 = Sheets(Z).Cells(1, Columns.Count).End(xlToLeft).Column
ActiveWorkbook.Sheets(Z).Range(Sheets(Z).Cells(1, 3), Sheets(Z).Cells(lastrow1, lastcolumn1)).Select
Selection.Cells.RowHeight = 9.14
Selection.Cells.ColumnWidth = 7.14
Next Z
End Sub
Userform code:
Private Sub CommandButton1_Click()
If Me.OptionButton5.Value = True Then
If Me.OptionButton7.Value = True Then
Call rowcolactivesheetb
ElseIf Me.OptionButton8.Value = True Then
rowcolallsheetb
End If
End If
If Me.OptionButton6.Value = True Then
If Me.OptionButton7.Value = True Then
Call rowcolactivesheetc
ElseIf Me.OptionButton8.Value = True Then
rowcolallsheetc
End If
End If
End Sub
First of all, I don't think I'd use OptionButtons. From your description it seems as if ListBoxes would suit you far better.
Secondly, it might be more elegant to pass the values into a single routine that actually sets the columns and rows rather than creating separate but almost identical routines.
I've stuck with your OptionButton structure and made the assumption that the three additional OptionButtons you allude to will be called OptionButton9, 10 & 11.
So the module code could be something like this:
Public Sub SizeRowsAndCols(fromB As Boolean, _
fromC As Boolean, _
targetActive As Boolean, _
targetAll As Boolean, _
excSheets As Variant)
Dim fromCol As Long
Dim sh As Worksheet
Dim nameString As Variant
'Define the column value
Select Case True
Case fromB: fromCol = 2
Case fromC: fromCol = 3
Case Else: MsgBox "Column selection error"
End Select
'Run routine on single or multiple sheets
Select Case True
Case targetActive
SetValuesOnSheet ThisWorkbook.ActiveSheet, fromCol
Case targetAll
For Each sh In ThisWorkbook.Worksheets
If IsEmpty(excSheets) Then
'If no sheets are to be excluded
SetValuesOnSheet sh, fromCol
Else
'Exclude the sheets in the list
For Each nameString In excSheets
If sh.Name <> nameString Then
SetValuesOnSheet sh, fromCol
End If
Next
End If
Next
Case Else
MsgBox "Sheet selection error"
End Select
End Sub
Private Sub SetValuesOnSheet(sh As Worksheet, fromCol As Long)
Dim lastR As Long, lastC As Long
Dim rng As Range
With sh
lastR = .Cells(.Rows.Count, "A").End(xlUp).Row
lastC = .Cells(1, .Columns.Count).End(xlToLeft).Column
Set rng = .Range(.Cells(1, fromCol), .Cells(lastR, lastC))
rng.RowHeight = 9.14
rng.ColumnWidth = 7.14
End With
End Sub
And the UserForm code might be:
Private Sub CommandButton1_Click()
Dim c As Long
Dim sheetNames As String
Dim list As Variant
'Build the list of excluded sheets
If OptionButton9.Value Then sheetNames = "Sheet1"
If OptionButton10.Value Then sheetNames = IIf(sheetNames <> "", "|", "") & "Sheet2"
If OptionButton11.Value Then sheetNames = IIf(sheetNames <> "", "|", "") & "Sheet3"
list = IIf(sheetNames <> "", Split(sheetNames, "|"), Empty)
'Call the generic routine
SizeRowsAndCols OptionButton5.Value, _
OptionButton6.Value, _
OptionButton7.Value, _
OptionButton8.Value, _
list
End Sub

VBA 'Vlookup' function operating on dynamic number of rows

I am not sure how to combine a Function with a Sub. Most likely, the Sub I have below needs corrections.
I have two tables in two separate sheets: Sheet1 and Sheet2.
Both tables have dynamic number of rows but the first rows always start in the same place and the number of columns in both tables is constant, too. Sheet1 data starts in A2 and ends in R2:R and Sheet2 data starts in A3 and ends in H3:H.
I am trying to implement VLOOkUP in column O of Sheet1, that would populate each cell in column O of Sheet1 with relevant values of column D in Sheet2. So far I managed to come up with code as below.
Public Function fsVlookup(ByVal pSearch As Range, ByVal pMatrix As Range, ByVal pMatColNum As Integer) As String
Dim s As String
On Error Resume Next
s = Application.WorksheetFunction.VLookup(pSearch, pMatrix, pMatColNum, False)
If IsError(s) Then
fsVlookup = ""
Else
fsVlookup = s
End If
End Function
Public Sub Delinquency2()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng As Range
Dim rCell As Range
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
pSearch = ws1.Range("D2:D" & Cells(Rows.Count, "A").End(xlDown).Row)
pMatrix = ws2.Range("$A3:$H" & Cells(Rows.Count, "C").End(xlDown).Row)
pMatColNum = 4
Set rng = ws1.Range("O2:O" & Cells(Rows.Count, "A").End(xlDown).Row)
For Each rCell In rng.Cells
With rCell
rCell.FormulaR1C1 = s
End With
Next rCell
End Sub
You will need to call the function in your sub using a similar line to below. It then takes your values from your sub and inputs them into the function and returns the value.
You need to dim the ranges in order for them to be recognized correctly in your function. I have updated your code to make it work and you can fiddle around with it to make it work the way you want it to. I also updated a few other spots to figure out the correct ranges, you don't want to use xlDown where you were using it, causes an enormous loop covering cells you don't want it to.
Public Function fsVlookup(ByVal pSearch As Range, ByVal pMatrix As Range, ByVal pMatColNum As Integer) As String
Dim s As String
On Error Resume Next
s = Application.WorksheetFunction.VLookup(pSearch, pMatrix, pMatColNum, False)
If IsError(s) Then
fsVlookup = ""
Else
fsVlookup = s
End If
End Function.
Public Sub Delinquency2()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim rng As Range
Dim rCell As Range, pMatrix As Range
Set ws1 = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
pSearchCol = ws1.Range("D2:D2").Column
Set pMatrix = ws2.Range("$A3:$H" & ws2.Cells(Rows.Count, "C").End(xlUp).Row)
pMatColNum = 4
Set rng = ws1.Range("O2:O" & ws1.Cells(Rows.Count, "A").End(xlUp).Row)
For Each rCell In rng.Cells
With rCell
rCell.Value = fsVlookup(ws1.Cells(rCell.Row, pSearchCol), pMatrix, pMatColNum)
End With
Next rCell
End Sub

Looping through all worksheets VBA

I am trying to loop through all the worksheets in the activeworkbook to perform a repetitive task.
I currently have the code below:
Sub sort_sectors()
Dim i As Integer
Dim rng As Range
Dim SortRng As Range
Dim rng1 As Integer
Dim ws As Worksheet
Dim wb As Workbook
Dim LastCol As Long
Dim LastRow As Long
Set wb = ActiveWorkbook
For Each ws In wb.Worksheets
'This is marking several of the sheets of which I do not want to run the sub
If ws.Range("a9").Value = "x" Then
NextIteration:
End If
'Reference point is rng1 to select the desired range
With Range("a1:t100")
rng1 = .Find(what:="sector", LookIn:=xlValues).Row
End With
'return the row number for the sector header
LastCol = ws.Cells(20, ws.Columns.Count).End(xlToLeft).Column
LastRow = ws.Range("a15").End(xlDown).Row
'I am going to add the code below to finish out the task that I want to complete
Next
End Sub
I am sure the problem is that I'm misunderstanding something about how the for each loop actually works. Hopefully someone's answer will allow to better understand.
I really appreciate any help on this.
I made some edits to the code, and now I actually do have an error :) I tried making the changes you suggested for the "with ws.range etc..." piece of the code, and I get the object error 91.
Below is my new and "improved" code.
Sub sort_sectors()
Dim i As Integer
Dim rng As Range
Dim SortRng As Range
Dim intAnchorRow As Integer
Dim intMktCapAnchor As Integer
Dim intSectorAnchor As Integer
Dim ws As Worksheet
Dim wb As Workbook
Dim LastCol As Long
Dim LastRow As Long
Set wb = ActiveWorkbook
For Each ws In ActiveWorkbook.Worksheets
'Filter out the sheets that we don't want to run
If ws.Range("a9").Value <> "x" Or ws.Name = "__FDSCACHE__" Or ws.Name = "INDEX" Then
'Get the anchor points for getting sort range and the sort keys
''''''THIS IS THE PART THAT IS NOW GIVING ME THE ERROR'''''''
With ws.Range("a1:t100")
intAnchorRow = .Find(what:="sector", LookIn:=xlValues).Row
intSectorAnchor = .Find(what:="sector", LookIn:=xlValues).Column
intMktCapAnchor = .Find(what:="Market Cap", LookIn:=xlValues).Column
End With
'Find the last row and column of the data range
LastCol = ws.Cells(20, ws.Columns.Count).End(xlToLeft).Column
LastRow = ws.Range("a15").End(xlDown).Row
Set SortRng = Range(Cells(intAnchorRow + 1, 1), Cells(LastRow, LastCol))
Range(SortRng).Sort key1:=Range(Cells(intAnchorRow + 1, intSectorAnchor), Cells(LastRow, intSectorAnchor)), _
order1:=xlAscending, key2:=Range(Cells(intAnchorRow + 1, intMktCapAnchor), Cells(LastRow, intMktCapAnchor)), _
order2:=xlDescending, Header:=xlNo
End If
Next
End Sub
Thanks again. This has been very helpful for me.
If I've understood your issue correctly, you don't want to use a worksheet with an x in cell A9.
If that's the case I would change the condition of the if statement to check if the cell does not contain the x. If this is true, it enters the rest of the code. If not, it goes to the next iteration.
Also, your NextIteration: doesn't do anything in the If statement.
Sub sort_sectors()
Dim i As Integer
Dim rng As Range
Dim SortRng As Range
Dim rng1 As Integer
Dim ws As Worksheet
Dim wb As Workbook
Dim LastCol As Long
Dim LastRow As Long
Set wb = ActiveWorkbook
For Each ws In wb.Worksheets
'This is marking several of the sheets of which I do not want to run the sub
If ws.Range("a9").Value <> "x" Then
'Reference point is rng1 to select the desired range
With Range("a1:t100")
rng1 = .Find(what:="sector", LookIn:=xlValues).Row
End With
'return the row number for the sector header
LastCol = ws.Cells(20, ws.Columns.Count).End(xlToLeft).Column
LastRow = ws.Range("a15").End(xlDown).Row
'I am going to add the code below to finish out the task that I want to complete
End If
Next
End Sub
The : operator is used to return the code to that line after a goto call.
For example
sub gotoEx()
for i = 1 to 10
if i = 5 then
goto jumpToHere
end if
next i
jumpToHere: '<~~ the code will come here when i = 5
'do some more code
end sub
And of course you can use this structure in your code if you wish, and have the jumpToHere: line just before the next
e.g.
for each ws in wb.Worksheets
if ws.Range("a9").Value = "x" then
goto jumpToHere
end if
'the rest of your code goes here
jumpToHere:
next

Dynamically set Ranges to the columns in VBA

I am importing some date to worksheet which needs to be ranged for validation and reference in other worksheets.
Say I have 4 columns in worksheet(WS1) but the row count is dynamic on every import. How can i range the columns(A:D)?
Please help.
Regards,
Mani
Use a lastRow variable to determine the last row. I included a few examples of this. Also on this example is a lastCol variable.. You can use this if the number of Columns is dynamic as well.
Private Sub lastRow()
Dim lastRow As Long
Dim lastCol As Long
Dim sheet As String
sheet = "WS1"
lastRow = Sheets(sheet).Range("A" & Rows.Count).End(xlUp).row 'Using Range()
lastRow = Sheets(sheet).Cells(Rows.Count, "A").End(xlUp).row 'Using Cells()
lastCol = Sheets(sheet).Cells(2, Columns.Count).End(xlToLeft).Column
End Sub
You can loop through your sheet easily enough using variables also. Using Cells(row,col) instead of Range(A1). you can use numbers or a letter in quotes for the column as shown in the example.
This example looks at WS1 and matches someValue. If the value in Column A of WS1 = somevalue, the record is copied to a "Master" Sheet.
Sub LoopExample()
Dim mRow As Long 'used for a master row
For lRow = 2 To lastRow
If Sheets(sheet).Cells(lRow, 1) = someValue Then
'perform something here like this. Copy columns A:D to the Master Sheet if match
For lCol = 1 To 4 'or you could go 1 to lastCol if you needed it dynamic
Sheets("MASTER").Cells(mRow, lCol) = Sheets(sheet).Cells(lRow, lCol) 'mRow as Row on Master
Next lCol
mRow = mRow + 1 'Increment the Master Row
End If
Next lRow
End Sub
Thanks anyways. But what i wanted was just to Name ranges the columns in worksheet.
I have already accomplished the copy and paste (Loading data b/w worksheets).
This is what i wanted.
vRowCount = DestWorkSheet.Cells(1, 1).SpecialCells(xlCellTypeLastCell).Row
vColCount = DestWorkSheet.Cells(1, 1).SpecialCells(xlCellTypeLastCell).Column
DestWorkSheet.usedRange.Columns.AutoFit
AddNamedRange Dest_RATES, DATA_Dest_RATES
Where AddNamedRange is a function,
Public Sub AddNamedRange(ByVal sheetCodeName As String, ByVal namedRange As String)
Dim rngToBeNamed As Range
Dim ws As Worksheet
On Error GoTo AddNamedRange_Error
Set rngToBeNamed = GetUsedRange(sheetCodeName)
Set ws = rngToBeNamed.Worksheet
ws.Names.Add name:=namedRange, RefersTo:=ws.Range(rngToBeNamed.Address)
On Error GoTo 0
Exit Sub
AddNamedRange_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure AddNamedRange of Module UtilitiesRange"
End Sub
Regards,
Mani
Seems like you could just use something like this in the sheet module:
Private Sub Worksheet_Change(ByVal target As Range)
Dim i As Long
Dim NamesOfNames(1 To 4) As String
NamesOfNames(1) = "NameOfColumn1"
NamesOfNames(2) = "NameOfColumn2"
NamesOfNames(3) = "NameOfColumn3"
NamesOfNames(4) = "NameOfColumn4"
For i = 1 To 4
ThisWorkbook.Names.Add Name:=NamesOfNames(i), _
RefersTo:=Range(Cells(1, i), Cells(Cells(Rows.Count, i).End(xlUp).Row, i))
Next i
End Sub