How to access data from another workbook? - excel

My function is accepting workbook as an input. But I am not able to iterate the values in the workbook.
I am getting the error as the 'Object variable not set'.
P.S: I cannot use Range since the range is unknown.
Sub Test (wb as workbook)
Dim sheet as worksheet
Dim value as Integer
sheet = wb.worksheet(1)
value = sheet.Cells(1,1).Formula
return value
End sub

There are several issues with your code:
The error you get is because you declared Dim sheet As Worksheet and Worksheet is an object and objects need to be Set like
Set sheet = wb.worksheet(1)
You defined the variable value as Integer and then you try to read the .Formula into it which is a String. An Integer cannot hold a formula. I think you wanted to read the .Cells(1,1).Value instead.
VBA does not have a return you need to set your function Test = value
You used a procedure Sub and not a function therefore it cannot return any values. You need to change it into a Function
I think you should end up with something like that:
Public Function ReadMyValue(ByVal wb As Workbook) As Double 'or Long
Dim ws As Worksheet
Set ws = wb.Worksheet(1) 'use set for objects!
Dim Value As Double 'or Long !!! but the same as your function is declared !!!
Value = ws.Cells(1, 1).Value
ReadMyValue = Value 'return the value to your function
End Function
And you can test it like:
Public Sub TestMyFunction()
Debug.Print ReadMyValue(ThisWorkbook) 'will output the value of A1 of the first worksheet in this workbook.
End Sub

Related

I'm getting an 'object required' error when calling a vba function

I'm getting a 424 "Object Required" when calling a vba function. The function parameter requires a range to change the name of the worksheet tab to be the same as a cell value.
Here is the code:
Dim new_wb As Workbook
Dim act_wb As Workbook
Dim stu_name As Object
Dim lastRow As Long
Dim currCol As Long
Dim nameRange As Range
Set act_wb = ActiveWorkbook
currCol = ActiveCell.Column
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
Set nameRange = Worksheets(1).Range("A4")
'begin vba code
ActiveWorkbook.Sheets(1).copy
Worksheet_Change (nameRange) 'error occurs here.
Here is the function signature of the function being called:
Private Sub Worksheet_Change(ByVal Target As Range)
If I remove the 'Set' keyword for nameRange, I get the following error
As the comment to your OP said, Worksheet_Change is not a standalone procedure you define, but rather an event handler that you declare to provide functionality in response to the Worksheet class raising the Change event behind the scenes. If you would like to change the name shown in a worksheet tab based on a cell value just extract the string from that cell and assign it to the corresponding property of the sheet object:
Dim wsName As String
wsName = Worksheets(1).Range("A4").Value
Dim wS As Worksheet
Set wS = ActiveWorkbook.Sheets(1).Copy
wS.Name = wsName
I am confused why the worksheet and cell references are always the same but you should be able to adjust from there if needed. Happy coding!

How can I convert a string variable to a worksheet object on VBA?

My problem is complex, I do not know if somebody has had this problem.
I want to convert a string ("Sheet1", "Sheet2", etc.) to an object in a particular case in a worksheet. With that, I can use a code Sheet3.Range("A1").Value, but I want to replace the number 3 with any I need.
I have a workbook with a worksheet called "CSV", in this sheet has a table called "t_csv". This worksheet is used to translate some text between Spanish and English.
CSV sheet example
I programed the routine to do that as follows (it works if you have the same order on the tab bar and VBA Index).
Sub tranlation()
Dim lang As String
Dim Sheet As Integer
Dim vcell As String
Dim data As String
‘Get the language from a buttom btn_esp or btn_eng from userform called “f_Login”
If f_Login.btn_esp.Value = True Then lang = "Español"
If f_Login.btn_eng.Value = True Then lang = "English"
‘Bucle to get the data from table “t_csv” located in sheet “CSV” (Sheet2)
For i = 1 To Sheet2.ListObjects("t_csv").DataBodyRange.Rows.Count
With Sheet2.ListObjects("t_csv")
Sheet = .ListColumns("Hoja").DataBodyRange(i).Value
vcell = .ListColumns("Celda").DataBodyRange(i).Value
'It uses the lang variable to choose the respective column on table "t_csv"
data = .ListColumns(lang).DataBodyRange(i).Value
‘write the text in corresnponding cell (code to be changed)
**Sheets(Sheet).Range(vcell).Value = dato**
End With
Next i
End Sub
The problem with the function Sheets() or Worksheets() is that they call the worksheets numbering the worksheets on the workbook's tab bar (from left to right). But I want to call the sheets with the VBA worksheet number because I have specific order on the workbook's tab bar.
Order in my tab bar
Order in my VBA index
I tried to use a code like this (it does not work because "Sheet" & Sheet is a string variable):
Dim SheetIndex As Worksheet
Set SheetIndex = “Sheet” & Sheet
SheetIndex.Range(vcell).value = data
But if I use the next code:
Dim HojaIndex As Worksheet
Set HojaIndex = Sheet3
HojaIndex.Range(vcell).value = data
This code works, but I want to substitute the number 3 automatically by whatever number I get from the table "t_csv".
I am guessing that the solution is to use the function CallByName, but I don't understand how can I use it. I saw some examples and I tried, but I could not make it work.
------- UPDATE ------- UPDATE ------- UPDATE ------- UPDATE ------- UPDATE ------- UPDATE -------
Thanks to #Tim Williams. I could solve my problem, I did some modifications inspired in his answer. I share the final answer if somebody has the same problem.
Function SheetByCodeName(SheetCodeName As String) As Worksheet
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.CodeName = SheetCodeName Then
Set SheetByCodeName = ws
Exit Function
End If
Next ws
End Function
Sub tranlation()
Dim lang As String
Dim Sheet As String 'Update variable type from Integer to String
Dim vcell As String
Dim data As String
Dim SheetName As Worksheet 'New variable to set the sheet name
‘Get the language from a buttom btn_esp or btn_eng from userform called “f_Login”
If f_Login.btn_esp.Value = True Then lang = "Español"
If f_Login.btn_eng.Value = True Then lang = "English"
‘Bucle to get the data from table “t_csv” located in sheet “CSV” (Sheet2)
For i = 1 To Sheet2.ListObjects("t_csv").DataBodyRange.Rows.Count
With Sheet2.ListObjects("t_csv")
Sheet = "Sheet" & .ListColumns("Hoja").DataBodyRange(i).Value
vcell = .ListColumns("Celda").DataBodyRange(i).Value
'It uses the lang variable to choose the respective column on table "t_csv"
data = .ListColumns(lang).DataBodyRange(i).Value
‘*-NEW CODE*- write the text in corresnponding cell
Set Sheet = SheetByCodeName(Sheet)
Sheet.Range(vcell).Value = data
‘*-NEW CODE*- write the text in corresnponding cell
End With
Next i
End Sub
Sub tester()
Debug.Print SheetByCodename("xxx").Name
End Sub
'Return a worksheet from its codeName (or Nothing if not match)
' Defaults to ThisWorkbook if a workbook is not passed
Function SheetByCodename(nm As String, Optional wb As Workbook = Nothing) As Worksheet
Dim ws As Object
If wb Is Nothing Then Set wb = ThisWorkbook
For Each ws In ThisWorkbook.Worksheets
If ws.CodeName = nm Then
Set SheetByCodename = ws
Exit Function
End If
Next ws
End Function

How to restrict VBA code to a specific worksheet?

I am trying to restrict the macro to a specific the Test! worksheet When I activate the macro pasted below and I am within the Test! worksheet, the macro works. However, when I try activating the macro on a different sheet, I receive an error. Any idea on what I need to do to modify the VBA code in order for it to be work any other worksheet within the workbook? Thanks
Sub Test ()
' Test Macro
'
' Keyboard Shortcut: Ctrl+Shift+B
'
Range ("Test!B1").Select
Selection.ClearContents
Range ("Test!B2").Select
Selection.ClearContents
Range ("Test!B3").Select
Selection.ClearContents
End Sub
You must reference the worksheet directly:
Dim ws as worksheet
set ws = Thisworkbook.Worksheets("Test")
with ws
.Range("B1:B3").ClearContents
end with
Other notes:
Don't use .Select or Selection. Simply operate on the objects directly.
You have String issues in your original code that would cause compile-time errors (fixed in my code)
You can clear the entire range in one line of code, rather than cell by cell
If the worksheet exists at compile-time in ThisWorkbook (i.e. the same file that's hosting the VBA code), then you don't need to retrieve the sheet at all - not by name, not by index.
VBA is already declaring a global Worksheet object variable for that sheet (and every sheet in ThisWorkbook). Verify the sheet's (Name) property; change it to TestSheet, then you can use TestSheet as an identifier anywhere in your code to refer to that sheet.
TestSheet.Range("B1:B3").ClearContents
And this code will work regardless of whether the user moved the sheet to another index/position in the workbook, or whether the user renamed the sheet's "tab" to something else.
ThisWorkbook.Worksheets(1).Range("B1:B3").ClearContents ' breaks if sheet is moved
ThisWorkbook.Worksheets("Test").Range("B1:B3").ClearContents ' breaks if sheet/tab is renamed
The Basics
If you want to learn something you should study versions 1 and 2, which are elaborate versions of versions 3 and 4 respectively.
Use Option Explicit to quickly find mistakes.
Use constants to be able to quickly change values.
Use object references to not have to type names on and on.
It is assumed that you will copy these codes in any module in the workbook where the worksheet 'Test' resides.
Option Explicit
Sub Test1()
Const cStrWs As String = "Test" 'Worksheet Name
Const cStrRng As String = "B1:B3" 'Range to 'DEL'
Dim oWs As Worksheet 'Worksheet Object
Dim oRng As Range 'Range Object
Set oWs = ThisWorkbook.Worksheets(cStrWs) 'Create a reference to the worksheet
Set oRng = oWs.Range(cStrRng) 'Create a reference to the range
oRng.ClearContents
End Sub
Sub Test2()
Const cStrWs As String = "Test" 'Worksheet Name
Const cStrCell As String = "B1" 'Cell to 'DEL'
Const cLngCells As Long = 3 'Number of cells
Dim oWs As Worksheet 'Worksheet Object
Dim oRng As Range 'Range Object
Set oWs = ThisWorkbook.Worksheets(cStrWs) 'Create a reference to the worksheet
Set oRng = oWs.Range(cStrCell) 'Create a reference to the cell range
oRng.Resize(3, 1).ClearContents
End Sub
Sub Test3()
ThisWorkbook.Worksheets("Test").Range("B1:B3").ClearContents
End Sub
Sub Test4()
ThisWorkbook.Worksheets("Test").Range("B1").Resize(3, 1).ClearContents
End Sub

Passing sheets as variables from one sub procedure to another in VBA

This is my first post on stackoverflow. I have two sub procedures in Excel VBA. The first one, called Sub IAR_part_2(), is intended to assign two sheets (by index location) to two variables named sheetname1 and sheetname2. after assigning the variables I am trying to pass them to my second sub procedure, called IAR_macro, to be processed. The two sheets are dependant on one another, so sheets 4 and 8 are ran through the IAR macro, sheets 5 and 9, sheets 6 and 10, etc. My problem is that I cannot figure out how to pass the sheetname variables from IAR_part_2 to IAR_macro. What am I doing wrong?
Sub IAR_part_2()
sheetname1 = Worksheets(4)
sheetname2 = Worksheets(8)
Call IAR_macro
End Sub
Sub IAR_macro(sheetname1 As Worksheet, sheetname2 As Worksheet)
Dim h As Long
Dim i As Long
Dim l As Long
Dim j As Long
Dim k As Long
Dim lr As Long
Worksheets(sheetname1).Activate
' Find the number of the last cell with data in column A and subtract 1 to populate variable i
On Error GoTo Canceled
i = (Range("B1").End(xlDown).Row) - 1
'Switch over to the Code sheet
Worksheets(sheetname2).Activate
'While the number of loops is less than variable i minus 1, copy the contents of cells A2 through A29 over and over down the worksheet
Do While l < (i - 1)
Range("A2:A29").Select
Selection.Copy
lr = Cells(Rows.Count, "A").End(xlUp).Row
Range("A" & lr + 1).Select
ActiveSheet.Paste
l = l + 1
'rest of macro follows from here...
Simple example of how to pass worksheet objects to a different sub:
Sub Macro1()
'Declare variables
Dim ws1 As Worksheet
Dim ws2 As Worksheet
'Assign variables to worksheet objects
Set ws1 = Worksheets(4)
Set ws2 = Worksheets(8)
'Call the second sub and pass the worksheet variables to it
Call Macro2(ws1, ws2)
End Sub
Sub Macro2(ByVal arg_ws1 As Worksheet, ByVal arg_ws2 As Worksheet)
'Reference the accepted arguments (in this case worksheet variables) directly:
MsgBox arg_ws1.Name
MsgBox arg_ws2.Name
'This will result in an error because you're using the passed argument incorrectly:
MsgBox ActiveWorkbook.Sheets(arg_ws1).Name '<-- Results in error
End Sub
You must reference the passed arguments directly. If you want to use the structure shown in your code, then the arguments passed need to be a string (but this method is NOT recommended):
Sub Macro1()
'Declare variables
Dim sSheet1 As String
Dim sSheet2 As String
'Assign variables to worksheet objects
sSheet1 = Worksheets(4).Name
sSheet2 = Worksheets(8).Name
'Call the second sub and pass the worksheet variables to it
Call Macro2(sSheet1, sSheet2)
End Sub
Sub Macro2(ByVal arg_sSheetName1 As String, ByVal arg_sSheetName2 As String)
'Because the arguments are strings, you can reference the worksheets this way
'This method is NOT recommended
MsgBox Worksheets(arg_sSheetName1).Name
MsgBox Worksheets(arg_sSheetName2).Name
End Sub
I noticed in your examples, you have your variables declared inside the function. Normally any variables you wish to use are better implemented by using option explicit. Also when identifying sheets, you will have less problems when addressing a sheet by its sheet number as opposed to the sheet name. That way if you need to use a variable, you can use just a integer instead as well.
Option Explicit
Dim h as Long, i as Long, l as Long, j as Long, k as Long, lr as Long
Dim x as Integer
Sub IAR_macro()
On Error GoTo Canceled
i = (Range("B1").End(xlDown).Row) - 1
Sheets(x).Activate
Do While l < (i - l)
Sheet ids can be located in the development tool. Here is an example:
This is minimal way of passing the worksheets. As far as they are objects, they are passed by reference by default:
Sub TestMe()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Set ws1 = Worksheets(1)
Set ws2 = Worksheets(2)
Passing ws1, ws2
End Sub
Sub Passing(arg_ws1 As Worksheet, arg_ws2 As Worksheet)
Debug.Print arg_ws1.Name
Debug.Print arg_ws2.Name
End Sub
VBA is not as easy and as simple as many people (mainly those who consider it to be a funny-scripting-language, written by wanna-be-developers) think. Sometimes it allows to write ByVal, but it follows its own rules and takes the argument ByRef, just to comfort you and make sure you are not going to make an error.
Saw the answer from #tigeravatar here and I have decided not to write a comment under it, but to explain in a different post why it is wrong and dangerous, as far as explaining it as a comment would have been tough.
If you try to write a Stop line here from the answer:
Sub Macro1()
'Declare variables
Dim ws1 As Worksheet
Dim ws2 As Worksheet
'Assign variables to worksheet objects
Set ws1 = Worksheets(4)
Set ws2 = Worksheets(8)
'Call the second sub and pass the worksheet variables to it
Call Macro2(ws1, ws2)
End Sub
Sub Macro2(ByVal arg_ws1 As Worksheet, ByVal arg_ws2 As Worksheet)
'Reference the accepted arguments (in this case worksheet variables) directly:
MsgBox arg_ws1.Name
Stop
MsgBox arg_ws2.Name
'This will result in an error because you're using the passed argument incorrectly:
MsgBox ActiveWorkbook.Sheets(arg_ws1).Name '<-- Results in error
End Sub
Run the answer and wait for the Stop line:
If the arg_ws1 were taken byVal, then if someone changes the name of the 8th worksheet, while the Stop is lighting, then it should still take the old name. It is ByVal, remember? Well, go ahead and change the name. Then continue with F5. Which name are you getting? Why?
The answer is because of the way the Sub is called with parenthesis in the arguments. These force ByVal and ignore anything explicitly written.
CPearson ByRef vs ByVal
Disclaimer - My Article for how to refer a function ByVal when it is ByRef
You need to pass the two variables you created to your second sub when you call the procedure:
Sub IAR_part_2()
Set sheetname1 = Worksheets(4)
Set sheetname2 = Worksheets(8)
Call IAR_macro (sheetname1,sheetname2)
End Sub

VBA pass the name of activesheet to a function

I have created a function to receive three parameters.:
An array of strings
A worksheet name
An index for the array of strings
When I compile I am getting a compile error stating:
Compile Error:
Expected:=
My call is:
Sub C_Button_ImportBOM_Click()
Dim strFilePathName As String
Dim strFileLine As String
Dim v As Variant
Dim RowIndex As Long
Dim mySheet As Worksheet
ActiveSheet.Name = "Import"
mySheet = Worksheets("Import")
strFilePathName = ImportFilePicker
v = QuickRead(strFilePathName)
For RowIndex = 0 To UBound(v)
PopulateNewLine (v(RowIndex), mySheet, RowIndex)
Next
End Sub
The function declaration is:
Function PopulateNewLine(SourceString As String, ImportSheet As Worksheet, CurrentRow As Long)
I have tried many things to no avail. Initially with just the first argument declared and used this worked okay.
Any ideas greatly appreciated.
Excel 2010 VBA
You are calling your function like a subroutine, i.e., you're not returning a value from it and assigning it to a variable, as you'd normally do with a function. That means you need to call it without the parentheses surrounding the arguments:
PopulateNewLine v(RowIndex), mySheet, RowIndex
If you really aren't returning anything from it, you should probably turn it into a Sub for clarity's sake:
Sub PopulateNewLine(SourceString As String, ImportSheet As Worksheet, CurrentRow As Long)
... your code here
End Sub
On another note, you should qualify your mySheet reference, and as I mentioned in the comments, use Set. It should look something like:
Set mySheet = ActiveWorkbook.Worksheets("Import")
Substitute whatever workbook Import is in for ActiveWorkbook above.

Resources