Setting Public variables in VBA Excel - excel

I am creating public variables in my program to be used across multiple subs in the module.
Public c As Range, wsSrc As Worksheet, wsDest As Worksheet, cDest As Range
However I want these variables to have the following value across the entire function as well
Set wsSrc = Workbooks("SourceBook").Worksheets("SourceSheet")
Set wsDest = Workbooks("DestBook").Worksheets("DestSheet")
Is there a way for me to set these and their values carry across the entire function?
Also for c and cDest I am changing those in the functions so I Do Not want those defined globally. Only wsSRC and wsDest. Are variables in VBA immutable?

This is a question of scope. For using variables across a module, you don't need to set them public - Public is used for making variables available to other modules.
In this case just need to declare variables outside function
'Module begining
Dim wsSrc As Worksheet, wsDest As Worksheet
Function f1
Dim c As Range, cDest As Range
'... wsSrc and wsDest are acessible, eg:
Set c = wsSrc.Columns(2).Find(What:="Commercial Income", LookIn:=xlValues, _ LookAt:=xlPart, MatchCase:=False)
End Function
Function f2
'... wsSrc and wsDest are also acessible here
End Function
However, to avoid initialization problems, you may use them as properties, so each time they are called, value is properly returned:
'Module begining
Property Get wsSrc As Worksheet
Set wsSrc = Workbooks("SourceBook").Worksheets("SourceSheet")
End Property
Property Get wsSrc As Worksheet
Set wsDest = Workbooks("DestBook").Worksheets("DestSheet")
End Property
Function f1
Dim c As Range, cDest As Range
'... wsSrc and wsDest are acessible
End Function
Function f2
'... wsSrc and wsDest are also acessible here
End Function

Related

having trouble with range union method

Having trouble with vba using range Union method. I have resolved it, at the end. But could not find the proper explanation, why it is so?
Set ws = ThisWorkbook.ActiveSheet
Set rng00 = Range("A1:C3")
Set rng01 = Range("F1:G3")
ws.Union(rng00,rng01).Copy 'the error giving line
When I have changed the line with below one strangely it has started to work. But as I remember I have used union method of range before with ThisWorkbook(or maybe Workbooks(name))
Worksheets("Sheet1").Activate
Application.Union(rng00,rng01).Copy
Application.Union feat. Qualifying Object References
Your 1st Code
Set ws = ThisWorkbook.ActiveSheet
Set rng00 = Range("A1:C3")
Set rng01 = Range("F1:G3")
ws.Union(rng00,rng01).Copy 'the error giving line
Your 2nd Code
Worksheets("Sheet1").Activate
Application.Union(rng00,rng01).Copy
Application.ThisWorkbook property
ThisWorkbook is (a reference to) the workbook containing this code. It is an exact workbook and there can only be one of it.
Dim wb As Workbook: Set wb = ThisWorkbook
Qualifying Worksheets
When referencing the sheets in a workbook, you want to qualify them (note the wb.):
Dim ws As Worksheet: Set ws = wb.Worksheets("Sheet1")
This can be only one worksheet, the worksheet named Sheet1 in the wb - workbook. It can't be the wrong worksheet unless you have incorrectly referenced the workbook.
When you do Set ws = ThisWorkbook.ActiveSheet, it creates a reference to any sheet (worksheet, chart) that is currently active (selected, you're looking at), yet in the line Worksheets("Sheet1").Activate you are activating Sheet1˛ of the ActiveWorkbook, the workbook which is the currently active (could be ThisWorkbook, but may not).
Qualifying Ranges
When referencing the ranges in a worksheet, you want to qualify them (note the ws.):
Dim rg1 As Range: Set rg1 = ws.Range("A1:C3")
Dim rg2 As Range: Set rg2 = ws.Range("F1:G3")
These are ranges in the ws - worksheet. They can't be the wrong ones unless you have incorrectly referenced the worksheet.
When you do Set rng00 = Range("A1:C3"): Set rng01 = Range("F1:G3"), you are creating references to ranges of the ActiveSheet (could be a worksheet, could be a chart), of the ActiveWorkbook (could be ThisWorkbook, but may not). It would have worked correctly only by chance in case your ActiveWorkbook was ThisWorkbook and your ActiveSheet was a worksheet named Sheet1 in the ActiveWorkbook.
Application.Union method
Its 'full name' says it all: it's a member of the Application object, not the Worksheet object. You can omit the Application. part from some of the Application object members: Union is such a member.
Union(rng1, rng2).Copy
Alltogether
Option Explicit
Sub CopyRange()
Dim wb As Workbook: Set wb = ThisWorkbook
Dim ws As Worksheet: Set ws = wb.Worksheets("Sheet1")
Dim rg1 As Range: Set rg1 = ws.Range("A1:C3")
Dim rg2 As Range: Set rg2 = ws.Range("F1:G3")
Union(rng1, rng2).Copy
End Sub
Of course, if there is no worksheet named Sheet1 in the workbook, an error will occur. But that's another story.
Union is a function of the Application Object, not of a Worksheet Object. Which means that ws.Union would not work because there is no member with the name Union as a member of ws. The correct parent is Application.Union but since it would be tedious to write Application. for every function, most of the time the Application. is not written.
The arguments of Union must be Range Objects that are from the same Worksheet. When declaring Ranges, it is important to explicitly declare their parent sheet, to avoid issues later with functions like Union. Adding ws in front of Range like ws.Range is how you can declare that the range is a member of that sheet.
So the full corrected code would be
Set ws = ThisWorkbook.ActiveSheet
Set rng00 = ws.Range("A1:C3")
Set rng01 = ws.Range("F1:G3")
Application.Union(rng00, rng01).Copy

Variable Range not recognized as a property of Worksheets

I'm trying to use both worksheets and ranges as variables, but I'm having some problems.
If I declare a worksheet as a variable and then use the range property it works just fine.
However, when I declare a variable Range and try to use it reference it, it throws me the error 438, object doesn't have property or method.
Sub try()
Dim ws As Worksheet
Set ws = Worksheets("Code")
ws.Range("A3", "B6").Value = "sheets"
Dim r As Range
Set r = Range("D1", "F3")
Worksheets("DATOS").r.Value = "ranges"
End Sub
My end goal would be to have both the Worksheet and the range as variables, so I could reference it such as
ws.r.Value = "123"
Thanks in advance, I hope my question isn't too basic and you can help me.
When you set a Range object, it is not a universal cell address to be used like what you did, each Range refers to a specific Worksheet that you can see under its Worksheet property. (documentation)
You did not specify the Worksheet in Set r = Range("D1", "F3") so VBA assumes that you are referring to the ActiveSheet which can be anything. (which is also why you are recommended to always fully qualify your range reference)
As mentioned in your comment - Since your objective is to use the same range for multiple worksheets, you can define the range address in a String variable and use that variable as shown below:
Sub try()
Const r As String = "D1:F3"
Worksheets("DATOS").Range(r).Value = "ranges"
Worksheets("Code").Range(r).Value = "ranges"
End Sub
You can't use range variable in this way. Rather qualify range mentioning sheet name. Try below codes.
Try below codes.
Sub try()
Dim ws As Worksheet
Set ws = Worksheets("Code")
ws.Range("A3", "B6").Value = "sheets"
Dim r As Range
Set r = Worksheets("DATOS").Range("D1", "F3")
r = "ranges"
End Sub
It is not possible to refer to the range in the way you want.
If you want to use a VBA variable to refer to ranges, you can do this:
' Get a range
Set Sht1 = ThisWorkbook.Worksheets("Sheet1")
Set Rng1 = Sht.Range("A2:B2")
' Set the contents of another range to the same value
Set Sht2 = ThisWorkbook.Worksheets("Sheet2")
Set Rng2 = Sht2.Range("C2:D2")
Rng2.value = Rng1.Value
You already seem to have a grasp of doing it this way.
If you want to refer to a range by a name, here is a method that creates a named range:
' Delete the named range if it exists and create it again.
Sub CreateNamedRange(Wbk As Workbook, Txt As String, Rng As Range)
On Error Resume Next
Wbk.Names(Txt).Delete
If Err.Number <> 0 Then Err.Clear
On Error GoTo 0
Wbk.Names.Add Txt, Rng
End Sub
Here we create a named range Name1 and retrieve it using Sht.Range("Name1"):
Sub CreateNamedRangeAndUseIt()
Dim Sht As Worksheet
Dim Rng As Range
' Set the value of the range to 42
Set Sht = ThisWorkbook.Worksheets("Sheet2")
Set Rng = Sht.Range("A2")
Rng.Value = 42
' Create a name for the range
CreateNamedRange ThisWorkbook, "Name1", Rng
' Activate some other sheet to make sure it works when the
' sheet with the named range is not active.
ThisWorkbook.Worksheets("Sheet1").Activate
' Get the named range and output the value of the range to the
' immediate window.
Set Rng = Sht.Range("Name1")
' This would also work, even though the named range does not
' exist on Sheet1:
' Set Rng = ThisWorkbook.Worksheets("Sheet1").Range("Name1")
' Or this (provided you don't have several workbooks open and
' another workbook is selected)
' Set Rng = Range("Name1")
Debug.Print Rng.Value
' Then select the range.
' We must activate the sheet first to select ranges in it.
Rng.Worksheet.Activate
Rng.Select
End Sub
The named range will still exist if you close and reopen the workbook, provided you save the workbook before closing it. So you only need to name the range once.

Select worksheet with non specific name

I am trying to execute a macro on a workbook that is downloaded from the web daily.
It has only one worksheet that changes names accordingly with the date downloaded (say: "vendas 201709294524455").
I need to set this worksheet as a variable. How can I do that with this dynamic name change?
I tried:
Dim Sh1 As Worksheet
Dim Sh2 As Worksheet
Dim FindCell As Range
Set Sh2 = ThisWorkbook.Worksheets("book1")
Set Sh1 = Workbooks("vendas.xlsm").Worksheets("*vendas*")
LastRow = Sh2.Range("d65536").End(xlUp).Row
'...rest of the code
As it only has one worksheet just use:
Set Sh1 = Workbooks("vendas.xlsm").Worksheets(1)
The Worksheets() allows for the index or a string name. With only one sheet the index is always 1 of that one sheet. See: https://msdn.microsoft.com/en-us/vba/excel-vba/articles/worksheet-object-excel
This will set Sh1 to the first worksheet in the workbook regardless of the name.
Scott Craner's solution is perfect for you. But just for anyone in future -- this is how you would do it if you want to avoid using the worksheet index (which changes if you change the order of the worksheets). Only worth using this method if you have more than one worksheet.
Sub SetWorksheet()
Dim ws As Worksheet, targetWorksheet As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.Name Like "*test*" Then
Set targetWorksheet = ws
Exit For
End If
Next ws
'use targetWorksheet
MsgBox targetWorksheet.Name
End Sub
If there are multiple sheets like "test" (in this example), you will get the first one.

VBA to delete multiple RangeNames for single Range

I have searched for an answer for hours, and can't search anymore. Have Seen lots of discussions about deleting all range names from a workbook or a worksheet. However, I need to delete multiple Range Names from a single Range while leaving other Range Names for other ranges on the same sheet and/or within the same workbook alone. Might the code look something like this?:
Sub Delete_My_Named_Ranges()
Dim nName As Name
Dim wbk As Workbook
Dim Sht As Worksheet
Dim rgNm As Range, aCell As Range
Set wbk = Workbooks("testRgNmDelete.xlsm")
Set Sht = wbk.Worksheets("Sheet1")
Set rgNm = Sht.Range("$A$1")
For Each nName In ThisWorkbook.Names
Set aCell = Range(nName)
If Not Intersect(aCell, rgNm) Is Nothing Then
nName.Delete
End If
Next
End Sub
OK, the above code works for a fixed range ("$A:$1"). But I need to be able to set rgNm as a variable instead of as a fixed range. here is an example, the error now is on the statement "Set aCell = Range(nName)."
Private Sub cboProductType_Change()
Dim wbSKUM As Workbook
Dim ws As Worksheet, wsLUL As Worksheet, wsLU As Worksheet
Dim rgPTL As Range, rgTable1 As Range, rgA1 As Range, rgA1LU As Range
Dim rgNm As Range, rgFormula As Range, aCell As Range
Dim sFormula As String
Dim nName As Name
Set wbSKUM = Workbooks("XBS_SKU_Master.xlsm")
Set ws = wbSKUM.Worksheets("SKUMaster")
Set wsLUL = wbSKUM.Worksheets("LookupLists")
Set wsLU = wbSKUM.Worksheets("Lookup")
Set rgPTL = wsLUL.Range("ProdTypeLookUp")
Set rgTable1 = ws.Range("Table1")
sFormula = "=SUBSTITUTE(SUBSTITUTE(F2,"" "",""_""),""-"","""")"
'clear Product Type Lookup List (Column D) to be sure no data remains
wsLUL.Activate
Range(Range("F2"), Range("F2").End(xlDown)).Select
Selection.Cells.Value = vbNullString
Set rgNm = Selection
For Each nName In ThisWorkbook.Names
Set aCell = Range(nName)
If Not Intersect(aCell, rgNm) Is Nothing Then
nName.Delete
End If
Next
Thanks again!
You are getting that error becuase you have not set rgName after declaring it.
Here is my understanding of your question.
Lets say there is a Range A1:A10 in Sheet1 and Cell A2 has a name NM1 and Cell A5 has a name NM2 and cell D10 has a name NM3
And you want a piece of code which deletes the Names in Range A1:A10 i.e NM1 and NM2 and not NM3
If the above is what you want then try this
Option Explicit
Sub Sample()
Dim rgName As Range, aCell As Range
Dim nName As Name
Set rgName = Sheets("Sheet1").Range("A1:A10")
For Each nName In ThisWorkbook.Names
Set aCell = Range(nName)
If Not Intersect(aCell, rgName) Is Nothing Then nName.Delete
Next
End Sub
And if I have misunderstood your question then you might want to rephrase it?
FOLLOWUP
This has been an extremely useful site to me as a new VBA user, but I have ABSOLUTELY no idea how to add to this site, beyond these little notes. Please try to imagine the Name Manager in Excel 2010 - in Name Manager there are say 10 unique names, all for =Sheet1!$A$1. I want all of these names which pertain to Sheet1!$A$1 to be deleted by VBA code. I DO NOT want other names to be deleted anywhere. – LostInData 3 mins ago
Based on your above comment try this
Option Explicit
Sub Sample()
Dim nName As Name
For Each nName In ThisWorkbook.Names
If nName.RefersTo = "=Sheet1!$A$1" Then nName.Delete
Next
End Sub
I think there is something missing in the solution given above.
In order to refer to the range of a named range with the range object you must use the name property of your named range. Therefore, not Set aCell = Range(nName) but rather Set aCell = Range(nName.Name).
Like this:
Option Explicit
Sub Sample()
Dim rgName As Range, aCell As Range
Dim nName As Name
Set rgName = Sheets("Sheet1").Range("A1:A10")
For Each nName In ThisWorkbook.Names
Set aCell = Range(nName.Name)
If Not Intersect(aCell, rgName) Is Nothing Then nName.Delete
Next
End Sub

How to Public Set - Public Declared variables?

I have a separate module to declare public variables
Public ws1, ws2 As Worksheet
On each module, on each procedure I must repeat the following :
Set ws1 = Sheets("Sun")
Set ws2 = Sheets("Moon")
...and then - write the code.
So, WHERE and HOW can I Set this variables so they are allready set on each module, on each Sub or Function - as Sheets Sun and Moon ?
Once you have it set it will remain available to the other modules. So I suggest you set it on Workbook_Open. Also your current code does not dimension ws1 as a worksheet, so I suggest you use
Standard Module
Public ws1 As Worksheet, ws2 As Worksheet
ThisWorkbook Module
Private Sub Workbook_Open()
Set ws1 = ThisWorkbook.Sheets("Sun")
End Sub

Resources