VBA Worksheet Sub Create Named Range in Another Worksheet - vba

I have a private sub that needs to create named ranges within another worksheet. It needs to stay a worksheet function, as it is a Worksheet_Change sub. I have successfully been able to set a range variable equal to a range on another sheet with this line:
Set rng2 = Sheets("Lists").Range(Sheets("Lists").Cells(2, Col), Sheets("Lists").Cells(Unique, Col))
However, when I put rng2 into the other portion of my code, it simply refers to the correct range within the Active Sheet.
Here is what I have tried:
ActiveWorkbook.Names.Add Name:="Level" & Col, RefersTo:= _
"= " & Sheets("Lists").Range(Sheets("Lists").Cells(2, Col), Sheets("Lists").Cells(Unique, Col)).Address & ""
and:
ActiveWorkbook.Names.Add Name:="Level" & Col, RefersTo:= _
"=" & rng2.Address & ""
The bottom function works when it is within a module stored inside the workbook as a whole, but again, does not work within a worksheet sub.
I have also tried Sheets("Lists").rng2.Address in the bottom attempt.

To have the address include the sheet's name, you have to set the external parameter:
rng2.address(external:=True)

Your RefersTo string needs to be something like "=Lists!A1". So all it's missing is the reference to the lists worksheet.
Try something like this:
Dim wsLists As Worksheet
Set wsLists = ThisWorkbook.Worksheets("Lists")
With wsLists
Set rng2 = .Range(.Cells(2, Col), .Cells(Unique, Col))
ThisWorkbook.Names.Add Name:="Level" & Col, RefersTo:="=" & rng2.Address(external:=True)
End With

Related

Cell Cross-Reference using VBA

I have done some searching and cannot figure out the syntax for what I would like to accomplish. I would like to have a specific, static cell on a Summary sheet automatically update to a dynamic cell on a different worksheet. I started with the following code:
Sheets("Summary").Activate
Range("B1").Select
ActiveCell.FormulaR1C1 = "='Current Billing'!R[46]C"
Range("B2").Select
ActiveCell.FormulaR1C1 = _
"='Current Billing'!R[45]C[3]+'Current Billing'!R[45]C[4]"
After getting some advice on a different topic for this same workbook, I now know .Select is a no-no and should be avoided. I also know that my last row on the "Current Billing" worksheet will change when I copy the code to another workbook for billing that project. This has led to me to modify my code to make it more fool-proof and versatile.
I have figured out how to get the the cell value to insert from my "Current Billing" sheet to my "Summary" sheet. But if something changes on the "Current Billing" sheet, the "Summary" sheet will not automatically update. This is the code I have that sort of works:
Dim ws5 As Worksheet
'ws5 is "Current Billing"
Dim ws6 As Worksheet
'ws6 is "Summary"
Dim LRowB2 As Long
Dim LRowB3 As String
LRowB2 = ws5.Cells(Rows.Count, "B").End(xlUp).Row
LRowB3 = ws5.Cells(LRowB2, "B")
ws6.Cells(1, "B").Formula = LRowB3
I tried these two code sequences, but kept getting errors:
Dim LRowB4 As String
Dim LRowE2 As Long
Dim LRowF2 As Long
LRowB4 = "= & ws5 & ! & LRowB3"
ws6.Cells(2, "B").Formula = "=sum(& LRowE2 &,& LRowF2 &)"
In short, is there a way to mimic the function of the first code procedure, but have the stability and fool-proofness of the second procedure? And, is there a way to integrate a =Sum() formula into the second procedure so I can add two cells from "Current Billing" on the "Summary" page? Thanks for the help!
The syntax of the formula is similar to =SUM('Current Billing'!C9, 'Current Billing'!D9)
This will check for errors, and is a bit more dynamic
Option Explicit
Public Sub UpdateSummary()
Dim wsCB As Worksheet, lrCB As Long, cbCellC As Range, cbCellD As Range
Dim wsSummary As Worksheet, sumCell As Range
Set wsCB = ThisWorkbook.Worksheets("Current Billing")
Set wsSummary = ThisWorkbook.Worksheets("Summary")
lrCB = wsCB.Cells(wsCB.Rows.Count, "C").End(xlUp).Row
'Verify that we have the same last row for col C and D on sheet "Current Billing"
If wsCB.Cells(wsCB.Rows.Count, "D").End(xlUp).Row = lrCB Then
Set cbCellC = wsCB.Cells(lrCB, "C") '"Current Billing".Cell(lr, C)
Set cbCellD = wsCB.Cells(lrCB, "D") '"Current Billing".Cell(lr, D)
Set sumCell = wsSummary.Cells(2, "B") '"Summary".Cell(2, B)
sumCell = "Invalid 'Current Billing' values" 'Default (in case of errors)
'Check "Current Billing" cells for errors (#N/A, #VALUE!, #REF!, #DIV/0!, etc)
If Not IsError(cbCellC) And Not IsError(cbCellD) Then
'Verify that "Current Billing" cells are numbers
If IsNumeric(cbCellC) And IsNumeric(cbCellD) Then
Dim cbC As String, cbD As String
cbC = "'" & wsCB.Name & "'!" & cbCellC.Address(0, 0) 'Current Billing'!C9
cbD = "'" & wsCB.Name & "'!" & cbCellD.Address(0, 0) 'Current Billing'!D9
'Final format: =SUM('Current Billing'!C9, 'Current Billing'!D9)
sumCell.Formula = "=SUM(" & cbC & ", " & cbD & ")" 'Update Summary cell
End If
End If
End If
End Sub
If the formula will contain a division, verify that the divisor is not 0
But if you are using VBA for this, you can simplify the syntax: sumCell = cbCellC + cbCellD
Note:
String variables become more complex when we have to use quotes inside quotes:
Str with ""double quotes"" can be built like this
str = "Str with """"double quotes""""", or
str = "Str with " & Chr(34) & """double quotes""" & Chr(34), or
str = "Str with " & Chr(34) & Chr(34) & "double quotes" & Chr(34) & Chr(34)
This string str = "Str with 'double quotes'" is Str with 'double quotes'
Use the Workbook_SheetChange event. It's an "event" action that is triggered when changes are made anywhere in your workbook. Within the event sub add code to update the value of your hard-coded/static cell with the value in your formula.
The Workbook_SheetChange sub must be in your workbook module (the default module name is "ThisWorkbook").
Here's an example of how you could use it. Just update the sheet names and range addresses for the variables myStaticCell and myFormulaCell.
In your ThisWorkbook Module:
Option Explicit
Public myVar As Variant ' stores last known value of static cell
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
' Define the locations of your formula cell and static cell.
Dim myStaticCell As Range
Dim myFormulaCell As Range
Set myStaticCell = Worksheets("Sheet2").Range("A2")
Set myFormulaCell = Worksheets("Sheet1").Range("A2")
' Ignore changes made directly to the static cell by comparing
' the sheet name and address where the workbook change
' occurred (variables Sh and Target which are built into the
' Workbook_SheetChange event) with the sheet name and address
' of your static cell.
If Sh.Name = myStaticCell.Parent.Name And _
Target.Address = myStaticCell.Address Then Exit Sub
' Save the value to a public variable so it can
' be checked the next time something changes.
myVar = myFormulaCell.Value
' If different, update the static cell with the
' value from your formula.
If myStaticCell <> myVar Then myStaticCell = myFormulaCell.Value
End Sub

Multiple Vlookups using VBA in one sub

I am trying to fill multiple columns in a sheet with vlookups from another sheet named "Go Live Data" in the same workbook, to the end of the range.
So, based off of the value starting in A6 in my sheet, I want to lookup to range A:K in sheet "Go Live Data" for cells starting in U6 to the end of the data filled in the tab (this will change dynamically). I want to repeat this for cells starting with V6 and W6.
This is the code that I have now, but it does not populate.
Sub VlookupGoLiveandBOP()
Dim Rng As Range, Dn As Range
Set Rng = Range(Range("A6"), Range("A" & Rows.Count).End(xlUp))
With Range("U6")
.Formula = "=IF(ISNA(VLOOKUP(A6,Go Live
Data!$A:$K,2,FALSE)),"""",VLOOKUP(A6,Go Live Data!$A:$K,2,FALSE))"
.AutoFill Destination:=Rng.Offset(, 66)
With Range("v6")
.Formula = "=IF(ISNA(VLOOKUP(A6,Go Live
Data!$A:$K,3,FALSE)),"""",VLOOKUP(A6,Go Live Data!$A:$K,3,FALSE))"
.AutoFill Destination:=Rng.Offset(, 66)
With Range("w6")
.Formula = "=IF(ISNA(VLOOKUP(A6,Go Live
Data!$A:$K,4,FALSE)),"""",VLOOKUP(A6,Go Live Data!$A:$K,4,FALSE))"
.AutoFill Destination:=Rng.Offset(, 66)
End With
Rng.Offset(, 66).Value = Rng.Offset(, 66).Value
End Sub
Am I on the wrong track? Thank you for your help.
Try the code below, it will help you assign the VLookup range correctly.
When using LKUpRng.Address(True, True, xlA1, xlExternal) the 4th parameter xlExternal adds also the sheet's name (and workbook if needed) with all the ' and ! needed.
Code
Option Explicit
Sub VlookupGoLiveandBOP()
Dim Rng As Range, Dn As Range
Dim LKUpRng As Range
Dim LkUpStr As String
Set LKUpRng = Sheets("Go Live Data").Range("A:K")
LkUpStr = LKUpRng.Address(True, True, xlA1, xlExternal) '<-- get the Range as a String, including the sheet's name
Set Rng = Range(Range("A6"), Range("A" & Rows.Count).End(xlUp))
Range("U6").Formula = "=IF(ISNA(VLOOKUP(A6," & LkUpStr & ",2,FALSE)),"""",VLOOKUP(A6," & LkUpStr & ",2,FALSE))"
End Sub

Set an Array with a variable name VBA

I am trying to use VBA to set an array based on a cell value in my workbook. I have an Array set on my data called THREEMO. For this month the value is Aug. I want to add a Sheet named Aug and then set an array for Columns B:C and name them Aug as well. I was able to add the sheet and name it but the array is not letting me use the variable to name the array. Here is what I have so far:
Dim WS As Worksheet
Set WS = Sheets.Add(After:=Sheets(Worksheets.Count))
WS.Name = Range("THREEMO")
Dim stNewTab As String
stNewTab = Range("THREEMO")
'Set Arrays
Columns("B:C").Select
ActiveWorkbook.Names.Add Name:=Range("THREEMO"), RefersToR1C1:="=Range("THREEMO")!C2:C3"
I am getting the error on the "'Set Arrays" section. This is what it looks like when I record the macro, so I am just having trouble setting up RefersToR1C1 with the variable for the month:
ActiveWorkbook.Names.Add Name:="AUG", RefersToR1C1:="=Aug!C5:C6"
Change
ActiveWorkbook.Names.Add Name:=Range("THREEMO"), RefersToR1C1:="=Range("THREEMO")!C2:C3"
to
ActiveWorkbook.Names.Add Name:=stNewTab, _
RefersToR1C1:="=" & stNewTab & "!C2:C3"
Note/Explanation:
Range("THREEMO") within quotes is considered as a string. Keep it outside the quotes. Better still use the variable stNewTab
The line
ActiveWorkbook.Names.Add Name:="AUG", RefersToR1C1:="=Aug!C5:C6"
can also be written as
ActiveWorkbook.Names.Add Name:="AUG", RefersToR1C1:="=" & "Aug" & "!C5:C6"
Now all you have to do is replace "Aug" with the variable.
Another option, you can set up the Range like this:
Dim Rng As range
Set Rng = WS.Range("C5:C6")
And then, you can use the Names.Add command a little different
ActiveWorkbook.Names.Add Name:=stNewTab, RefersTo:=Rng

With For each loop in Excel VBA how do I reference the cell address within the formula?

The code I commented out originally required a user to drag down the formula once the formula was appended to a cell. I have revised the procedure below and I switched from a For each cell loop - could this have been done with a For each loop structure? Utilizing Cell.address or something along the lines? Please assume my variables are all defined.
Dim client_row As Long
'Dim v As Long
Dim v As Variant
Dim i As Integer
i = 2
client_row = 0
LASTROW2 = Range("B" & Rows.Count).End(xlUp).Row
Set rng2 = Range("N2:N" & LASTROW2)
' For Each cell In rng2
' If cell.Offset(0, -13) <> "" Then
' cell.Formula = "=IFERROR(TEXT(IF(F2=""GBP"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2/100,IF(E2=""EQ"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2,($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2*100)),""0.00""),""PLEASE REVIEW"")"
' Debug.Print cell
' End If
' Next cell
For Each v In rng2
If v.Offset(0, -13) <> "" Then
v.Formula = "=IFERROR(TEXT(IF($F" & i & "=""GBP"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2/100,IF(E2=""EQ"",($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2,($H2-(IF(LEN($C2)=7,BDH($C2&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH($C2&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/$H2*100)),""0.00""),""PLEASE REVIEW"")"
i = i + 1
Debug.Print i
End If
Next v
In this case you could use the rng2.FormulaR1C1 property instead. It allows you to specify a relative reference which means you won't need to keep track of the current row.
The commented out section could be written as below:
Set rng2 = Range("N2:N" & LASTROW2)
For Each cell In rng2
If cell.Offset(0, -13) <> "" Then
cell.FormulaR1C1 = "=IFERROR(TEXT(IF(RC[-8]=""GBP"",(RC[-6]-(IF(LEN(RC[-11])=7,BDH(RC[-11]&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH(RC[-11]&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/RC[-6]/100,IF(RC[-9]=""EQ"",(RC[-6]-(IF(LEN(RC[-11])=7,BDH(RC[-11]&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH(RC[-11]&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/RC[-6],(RC[-6]-(IF(LEN(RC[-11])=7,BDH(RC[-11]&"" ""&""SEDOL"",""PX_LAST"",TODAY()),BDH(RC[-11]&"" ""&""CUSIP"",""PX_LAST"",TODAY()))))/RC[-6]*100)),""0.00""),""PLEASE REVIEW"")"
Debug.Print cell
End If
Next cell
As stated here in your linked questions, you could also use v.Column to get the column of the current cell in the loop. There is no special structure like For Each cell In range. cell is in this scenario just an object variable pointing to a cell, just like v in your example.
To make your life a little bit easier just declare v as Range, then IntelliSense should show you possible properties and methods for v.
Luke's answer is another nice way to set relative addresses though ;)

Application defined or object defined error on code

I get an error on this line in my code, any ideas what the issue may be?
Intersect(.UsedRange, .UsedRange.Offset(1)).SpecialCells(12).EntireRow.Delete
Here is the rest of the code:
Sub DefineDL_IDL()
Dim wbTHMacro As Workbook, wsRegulares As Worksheet, wsRegularesDemitidos As Worksheet, wsTempActivos As Worksheet, _
wsTempJA As Worksheet, wsTempFit As Worksheet, wsTempDemitidos As Worksheet, wsPS As Worksheet, wsResultados As Worksheet, _
wsDLList As Worksheet, wssheet As Worksheet, count_DL As Integer, count_IDL As Integer
Dim x&, r As Long
'*************REGULARES***********
Sheets("Regulares").Select
'Debug.Print xlToRight
'Sheets("Raw").Copy before:=Sheets(2)
With Sheets("Regulares")
'.Name = "Final2"
.UsedRange.AutoFilter 9, "INATIVE"
Intersect(.UsedRange, .UsedRange.Offset(1)).SpecialCells(12).EntireRow.Delete
r = WorksheetFunction.CountA(.Range("A:A"))
.UsedRange.AutoFilter
.Range("J:J").Insert xlToRight
.Range("J1").Value = "Real MO"
.Range("K:K").Cut
.Range("I:I").Insert xlToRight
.Range("Q:Q").Cut
.Range("I:I").Insert xlToRight
.Range("L2:L" & r).FormulaR1C1 = "=VLOOKUP(RC[-3],'DL List'!C[-11]:C[-10],2,0)"
.Range("L2:L" & r).Value = .Range("L2:L" & r).Value
For x = 2 To r
If Range("L" & x).Text = "#N/A" Then
'If Range("K" & x).Value = "DL" Then
' Range("L" & x).Value = "DL"
'Else: Range("L" & x).Value = "IDL": End If
Range("L" & x).Value = "IDL"
End If
Next x
End With
count_DL = Application.WorksheetFunction.CountIf(ActiveSheet.Range("L:L"), "DL")
count_IDL = Application.WorksheetFunction.CountIf(ActiveSheet.Range("L:L"), "IDL")
Worksheets("Resultados").Range("B17") = count_DL
Worksheets("Resultados").Range("C17") = count_IDL
Your expression works on my test worksheet so the problem must be something about your data.
I do not like stringing properties together like this because the objective becomes very unclear. Worse, if it fails, you do not know where is fails.
Try replacing the statement with this:
Dim rng As Range
Debug.Print .UsedRange.Address
Debug.Print .UsedRange.Offset(1).Address
Set rng = Intersect(.UsedRange, .UsedRange.Offset(1))
Debug.Print rng.Address
Debug.Print rng.SpecialCells(12).Address
Debug.Print rng.SpecialCells(12).EntireRow.Address
rng.SpecialCells(12).EntireRow.Delete
Step through this code to make sure each range is as you expect.
My guess that that there are no visible cells in the range so you are attempting to delete Nothing.
Edit Extra information about finding last row of worksheet.
There are a variety of methods of finding the last used row or column of a worksheet. None work in every situation but UsedRange is the method least likely to give the result you expect.
The most popular method of finding the last row, judging by answers here, is:
RowLast = .Cells(Rows.Count,9).End(xlUp).Row
This is the VBA equivalent of placing the cursor in the bottom cell of column 9 and clicking Ctrl+Up. RowLast will be set to the last row with a value in column 9 unless you have a value in the bottom cell. For this method to be of any use, there must be a value in the specified column of the last used row.
Find is a reliable method of finding the last value by either row or column.
SpecialCells is another useful method.
This answer of mine VBA Dynamic Ranges includea a macro, FindFinal, which demonstrates how these methods can fail to give the result you might expect. If you wish to fully understand the issues, step through this macro studying what happens.