Set up variable to refer to sheet - excel

Can I set up a variable x = "Sheet1"
In order to do:
x.Range("A3")
instead of
Sheet1.Range("A3")?
What type of variable should it be? I tried string and it didn't work.
Thanks
Update:
I would like a method that won't be affected by changing the worksheet name. i.e.
Sheet1.Range("A3") will always refer to the same worksheet even if I change the worksheet name to "peanuts", at least that's what I thought.

You want to declare it a Worksheet Object:
Dim x as WorkSheet
Since it is an object we must Set the sheet:
Set x = WorkSheets("Sheet1")
or if you want to use the code name:
Set x = Sheet1
Then yes you can use it:
x.Range("A3")...

I think you're mixing two different declarations
Declaring a sheet (must set the sheet)
dim ws as worksheet
set ws = sheets("sheet1")
ws.cells(1,1).value = ""
Declaring a string as the name of a sheet (can utilize the NAME of the sheet as a string)
dim ws_name as string
ws_name = "sheet1"
sheets(ws_name).cells(1,1).value = ""
Additionally you could use the sheet index, which does not utilize the name of the sheet (if you change it later); this is a little different than the previous two, but this example (using a loop) helps more clearly explain how the index can be utilized
dim i as long
for i = 1 to sheets.count step 1
sheets(i).cells(1,1).value = ""
next i
and a simple use of a sheet index
sheets(1).cells(1,1).value = ""

It should be worksheet. The syntax would be:
Dim x as Worksheet
Set x = Sheet1
after that you can use x.range("A3") to refer to Cell A3 in sheet1

You want a Worksheet variable. The "name" you're referring to is the Name property, and that isn't in code; it's the "tab name" of the sheet, that the user can change on a whim and that, for that reason, you don't want to have to hard-code anywhere if you can avoid it.
But it looks like you're referring to a sheet that exists in ThisWorkbook at compile-time; each sheet has a (Name) property that you can edit in the Properties toolwindow (F4). By default that name is Sheet1, and VBA takes that name and makes it a project-scope identifier you can use anywhere you need to refer to that particular sheet.
So if you change a sheet's (Name) to PeanutsSheet, then you can use PeanutsSheet in your code:
PeanutsSheet.Range("A1").Value = 42 '<~ that's the "CodeName", and users can't easily break it
And that is preferable to referring to that same sheet by its "tab name":
ThisWorkbook.Worksheets("Peanuts").Range("A1").Value = 42 '<~ breaks if tab is ever renamed
Declaring a variable for a sheet that exists in ThisWorkbook at compile-time, is completely redundant:
Dim ws As Worksheet
Set ws = Sheet1 '<~ variable 'ws' is 100% redundant
Using such variables makes the code confusing and harder to follow than it needs to be, because now you have 2 (or more) identifiers referring to the same thing.
But this is worse:
Set ws = ThisWorkbook.Worksheets("Sheet1") '<~ now it's redundant *and* super frail

Related

Difference between rng.RefersToRange.Parent.Name and rng.Parent.Name

I need to loop through named ranges of my workbook and check if they are in a specific Worksheet and then do something to it. Found a suggestion to use something like this:
Dim ws As Worksheet, rng As Name
Set ws = somews
For Each rng In ThisWorkbook.Names
If rng.RefersToRange.Parent.Name = somews.Name Then
'do stuff
End If
Next rng
At the beginning that worked fine. Now i have a problem. Somebody else uses this workbook as well and i don't know what they did. The workbook has a new sheet with named ranges, which are identical to some of my ranges in somews, but they refer to some outside source. I guess they were coping stuff between workbooks. The scope of my ranges is for entire workbook and the scope for those new ranges in only for that new sheet. I knew this would happen and that is why i needed this code.
This code breaks now. Because if rng is form the new sheet rng.RefersToRange.Parent.Name still returns the name of my sheet instead of the new sheet. So the If statement gets executed, but with wrong range. If i however use rng.Parent.Name i get the correct name.
What is the difference here? When should i use one and when the other? Is there a better way?
.RefersToRange is not a member of a Range Object. It is a member of a Name Object.
The .RefersToRange object is how you would access the Range Object that is associated with the Name Object. Once you have a Range Object, the .Parent property will return the Worksheet Object that the range belongs to. The .Name property will return the name of the worksheet.
So if rng is a Name Object, rng.RefersToRange.Parent.Name returns the name of the worksheet that contains the range that the name is associated with.
On the other side rng.Parent.Name doesn't go through the range, and is therefore less specific about which name it returns. The .Parent property is applied to the Name Object, which can refer to either a worksheet or the workbook. So depending on how the scope was defined when the Name Object was created, the .Parent.Name will return the name of either a single Worksheet or the Workbook.

I need to get the CODENAME (first) of a Worksheet so then I can check its NAME [duplicate]

This question already has answers here:
Refer to sheet using codename
(5 answers)
Excel tab sheet names vs. Visual Basic sheet names
(10 answers)
Closed 2 years ago.
I have a Workbook with 6 Worksheets.
Imagine "Alpha", "Beta", "Gama", "Delta", "Epsilon", "Zeta".
In this order, they are indexed as Alpha= 1 - Beta = 2... Zeta = 6
BUT, if I move Alpha to a position AFTER Beta, the Index number will also change, so I should not refer to the WS by their index numbers.
Now, If the user changes the Worksheet NAME, the code - if setting WS by the worksheet NAME - will also fail.
The only thing that is not "normally" changeable by the user is the sheets CODE.NAME.
Again...
Codename "Sheet1" - Name "Alpha" - Index "1"
Codename "Sheet2" - Name "Beta" - Index "2"
Codename "Sheet3" - Name "Gama" - Index "3"
... and so on.
Even if I change the sheet's position (index) or the sheet's NAME, if I refer to them by their CODE.NAME, it doesn't matter in what position they are located or what their names are.
So, all I want is picking up the Sheet's NAME, using it's CODE.NAME to get there...
Something like this:
Dim WB1 as Workbook
Dim WS1 as Worksheet
Dim WSNAME as String
Set WB1 = ThisWorkbook
Set WS1 = (here I need code to pick the "Sheet1" worksheet to variable WS1) (*)
WSNAME = WS1.Name
Pay attention: The final result should store in WS1 the sheet's NAME ("ALPHA"). If the user has changed that name, it should still work, storing the new name. I must FIRST get the sheet by it's CODE.NAME (unchangeable) so then I could check its NAME.
(*) I've tried...
Set WS1 = WB1.Sheet1
Set WS1 = WB1.Sheet1.CodeName
Set WS1 = WB1.Sheets(Sheet1)
Set WS1 = WB1.Sheets("Sheet1").CodeName
Nothing worked...
But if I use
Set WS1 = WB1.Sheets("ALPHA").CodeName
it works, but then I have the same problem... If the user changes "ALPHA" to "ALFA" everything stops working...
Any help would be greatly appreciated.
Thanks in advance!
--- Edited to add images -------------------------------------
My Excel is in Portuguese, so instead of "Sheet1", "Sheet2", ... it creates worksheets as "Planilha1", "Planilha2", "Planilha3"... and these "Planilhas" are named with whatever the user wants...
Look at this image:
The CODE.NAME for the highlighted sheet is "Planilha5", and the NAME is "Cardiac variables". I need to find "Cardiac variables" in a table (in another Worksheet), to fetch the LINE NUMBER in that table, where all references for this worksheet are stored. If I do a simple lookup in the table's column where the worksheets names are, and search for "Cardiac variables" it would work A-OK.
See, I find what I'm looking for ("Cardiac variables") in line 15!
But if the user changes the worksheet NAME...
Back to the first image. Look at the code to the right. (HEALTHY was previously DIM as ThisWorkbook) There's an entry that says ESTAABA = "Cardiac variables", and then I set WS3 as being the contents of ESTAABA (I could have done directly...). But I don't want to keep the worksheet's NAME in the code, because if the user changes the TAB name (from "Cardiac variables" to "Cardio VAR" as the example above) it won't work anymore.
BUT, if I lookup in the previous table, where "Planilha5" occurs, all would be fine, regardless of the NAME the user wants to use to the worksheet.
The problem is that I am not being able to set WS3 to be understood as the Planilha5 worksheet. If I say Set WS3 = HEALTHY.Sheets("Cardiac variables") it works. If I say Set WS3 = HEALTHY.Planilha5, or = Planilha5, or = Sheets(Planilha5) it doesn't work.
In resume, I don't want to use "Cardiac variables" (or the other NAMEs of the Worksheets - NAMEs can be altered...) anywhere in my code.
I want to set WS3 to the Planilha5 worksheet.
Forget the reference "Cardiac Variables", and forget the worksheet index number, for as the user can change names, he can also change the worksheet's position (by moving or addin/removing previous worksheets).
Thanks for the patience!
I'm not sure if I get your question correctly. But here is what you want as per my understanding.
Add this function in a Module
Function GetSheetByCodeName(ByVal CodeName As String) As Worksheet
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.CodeName = CodeName Then
Set GetSheetByCodeName = ws
Exit Function
End If
Next
End Function
And then you can easily call this function whenever you need to refer to any sheet.
e.g.
'-- test function
Sub Test()
Dim ws As Worksheet
Set ws = GetSheetByCodeName("Sheet1")
MsgBox ws.CodeName & " ----- " & ws.Name
End Sub
And this is what you see...

Set variable to a sheet's Codename, Using it in the code isn't working

This works, referring to the codename directly (Sheet3)...
Sheet3.Shapes(PopUp).Visible = True
...but using a variable set to the same Codename value doesn't...
WS = ActiveSheet.CodeName
WS.Shapes(PopUp).Visible = True
Why? I ask because I need to extend the larger macro's functionality to the whole workbook, so I'm hoping the variable will let this apply to whatever worksheet the user happens to be on.
Thanks for any guidance!
If you Dim the variable ws as a string, it will not work as a sheet object.
If you have changed the (Name) property of the sheet to "MyCodename", for example, you can just use that codename as the sheet object
MyCodename.Shapes(PopUp).Visible = True
.. assuming that the variable popup is of the proper type and initialised with the proper value.
If your goal is to pass a worksheet and work off that, then pass a worksheet:
Sub DoSomething(ByVal ws As Worksheet, ByVal popup As String)
ws.Shapes(popup).Visible = True
End sub
And then you can use the code name to get the specific worksheet object you want to pass that procedure:
DoSomething Sheet1, "shape1"
DoSomething Sheet2, "shape12"
DoSomething Sheet3, "SomeButton"
I just want a sheet's macro to pass the variables for its Codename
When used like above, the code name is a Worksheet variable. VBA uses the string value of the Worksheet.CodeName property to generate a project-scoped object variable by that name - and indeed, using these "free" variables whenever you need to refer to any sheet that exists at compile-time, is a thousand times better than dereferencing that exact same object reference through the Sheets or Worksheets collection using the sheet's Name, which your user can change on a whim at any given time (unless workbook structure is protected), and break your code!
On the other hand...
[...] apply to whatever worksheet the user happens to be on
This is the textbook definition of ActiveSheet - if you need to work on whatever worksheet the user happens to be on, then you use ActiveSheet, which refers to exactly that.

VB.NET Gembox - Switch Active Spreadsheet

I've looked all over for this and think I'm just hitting a mental brick wall for something simple. But still. I'm writing a quick program to help me with some mileage spreadsheets for different vehicle. Each vehicle has its on worksheet within the spreadsheet, I'm using GemBox in VB.net.
Basically, dending on which button you press it chooses the correct sheet for the corresponding vehicle. I cannot find anything, anywhere that tells me how to choose a different existing sheet as the active worksheet.
This is my test code atm.
Public Sub SetMiles(vehicle As String)
Dim wb = ExcelFile.Load(file)
Dim ws = wb.Worksheets.ActiveWorksheet(vehicle)
loc = "F12"
ws.Cells(loc).Value = "800"
End Sub
In GemBox.Spreadsheet you don't need to set the sheet as active in order to use it.
In other words, let's say you have an Excel file which has "Sheet1" and "Sheet2". To write into those sheets you can use the following:
Dim wb = ExcelFile.Load(File)
Dim ws = wb.Worksheets("Sheet1")
ws.Cells("A1").Value = "Foo"
ws = wb.Worksheets("Sheet2")
ws.Cells("A1").Value = "Bar"
You can also use the following:
Dim wb = ExcelFile.Load(File)
Dim ws = wb.Worksheets(0)
ws.Cells("A1").Value = "Foo"
ws = wb.Worksheets(1)
ws.Cells("A1").Value = "Bar"
So, I believe that what you need is the following:
Public Sub SetMiles(vehicle As String)
Dim wb = ExcelFile.Load(File)
Dim ws = wb.Worksheets(vehicle)
Loc = "F12"
ws.Cells(Loc).Value = "800"
End Sub
Last, in case you do need to set some sheet as active, then you can do that with GemBox.Spreadsheet as following:
wb.Worksheets.ActiveWorksheet = wb.Worksheets(vehicle)
However, again GemBox.Spreadsheet doesn't care if the sheet is active or not, you can access and modified it regardless of that. By setting the sheet as active, that sheet will be the first visible one that you see when you open that file in an Excel application.
wb.Sheets(vehicle).Activate is the simplest way.
Although I recommend that you also validate the vehicle string to ensure that Sheet actually exists. You can then either ignore, display a message or create a new sheet.
I was assuming that you wanted to activate the sheet so that the user can do manual input. If you are doing automated input (no user interaction), then you are better off not activating the sheet. Something along the lines of:
Public Sub SetMiles(vehicle As String, wb as Workbook, loc as string, Mileage as string)
' passing the values is better encapsulation for OOP purposes
' in your example above, loc="F12", Mileage = "800"
' You have passed the Mileage as a string - but you could also use a numeric value.
' Validate sheet name here if important.
' Validate range name here if important.
wb.Sheets(vehicle).Range(loc).Value = Mileage
End Sub
Edit: Appears GemBox uses Worksheets instead of Sheets.

Reference excel worksheet by name?

I have the name of a worksheet stored as a string in a variable. How do I perform some operation on this worksheet?
I though I would do something like this:
nameOfWorkSheet = "test"
ActiveWorkbook.Worksheets(nameOfWorkSheet).someOperation()
How do I get this done?
There are several options, including using the method you demonstrate, With, and using a variable.
My preference is option 4 below: Dim a variable of type Worksheet and store the worksheet and call the methods on the variable or pass it to functions, however any of the options work.
Sub Test()
Dim SheetName As String
Dim SearchText As String
Dim FoundRange As Range
SheetName = "test"
SearchText = "abc"
' 0. If you know the sheet is the ActiveSheet, you can use if directly.
Set FoundRange = ActiveSheet.UsedRange.Find(What:=SearchText)
' Since I usually have a lot of Subs/Functions, I don't use this method often.
' If I do, I store it in a variable to make it easy to change in the future or
' to pass to functions, e.g.: Set MySheet = ActiveSheet
' If your methods need to work with multiple worksheets at the same time, using
' ActiveSheet probably isn't a good idea and you should just specify the sheets.
' 1. Using Sheets or Worksheets (Least efficient if repeating or calling multiple times)
Set FoundRange = Sheets(SheetName).UsedRange.Find(What:=SearchText)
Set FoundRange = Worksheets(SheetName).UsedRange.Find(What:=SearchText)
' 2. Using Named Sheet, i.e. Sheet1 (if Worksheet is named "Sheet1"). The
' sheet names use the title/name of the worksheet, however the name must
' be a valid VBA identifier (no spaces or special characters. Use the Object
' Browser to find the sheet names if it isn't obvious. (More efficient than #1)
Set FoundRange = Sheet1.UsedRange.Find(What:=SearchText)
' 3. Using "With" (more efficient than #1)
With Sheets(SheetName)
Set FoundRange = .UsedRange.Find(What:=SearchText)
End With
' or possibly...
With Sheets(SheetName).UsedRange
Set FoundRange = .Find(What:=SearchText)
End With
' 4. Using Worksheet variable (more efficient than 1)
Dim MySheet As Worksheet
Set MySheet = Worksheets(SheetName)
Set FoundRange = MySheet.UsedRange.Find(What:=SearchText)
' Calling a Function/Sub
Test2 Sheets(SheetName) ' Option 1
Test2 Sheet1 ' Option 2
Test2 MySheet ' Option 4
End Sub
Sub Test2(TestSheet As Worksheet)
Dim RowIndex As Long
For RowIndex = 1 To TestSheet.UsedRange.Rows.Count
If TestSheet.Cells(RowIndex, 1).Value = "SomeValue" Then
' Do something
End If
Next RowIndex
End Sub
The best way is to create a variable of type Worksheet, assign the worksheet and use it every time the VBA would implicitly use the ActiveSheet.
This will help you avoid bugs that will eventually show up when your program grows in size.
For example something like Range("A1:C10").Sort Key1:=Range("A2") is good when the macro works only on one sheet. But you will eventually expand your macro to work with several sheets, find out that this doesn't work, adjust it to ShTest1.Range("A1:C10").Sort Key1:=Range("A2")... and find out that it still doesn't work.
Here is the correct way:
Dim ShTest1 As Worksheet
Set ShTest1 = Sheets("Test1")
ShTest1.Range("A1:C10").Sort Key1:=ShTest1.Range("A2")
To expand on Ryan's answer, when you are declaring variables (using Dim) you can cheat a little bit by using the predictive text feature in the VBE, as in the image below.
If it shows up in that list, then you can assign an object of that type to a variable. So not just a Worksheet, as Ryan pointed out, but also a Chart, Range, Workbook, Series and on and on.
You set that variable equal to the object you want to manipulate and then you can call methods, pass it to functions, etc, just like Ryan pointed out for this example. You might run into a couple snags when it comes to collections vs objects (Chart or Charts, Range or Ranges, etc) but with trial and error you'll get it for sure.

Resources