VBA Use variable value across modules - excel

I'm trying to use the name of a workbook which I set in module 1, across other private modules but I'm getting different errors depending on how I set it up. I added comments in the code that explain what happens in the different scenarios.
Option Explicit
Sub TestSharedVars()
CopyCellsthenClose
OpenNewWksheet (AlphaExportBook)
' *** Like this
' OpenNewWksheet (AlphaExportBook) I get "Error Variable not defined"
' *** Like this
' OpenNewWksheet I get "Error Argument not optional"
CloseWkbook
End Sub
Private Sub CopyCellsthenClose()
Dim AlphaExportBook As Workbook
Dim theRows
Dim theColumns
With ActiveSheet.UsedRange
theRows = .Rows.Count
theColumns = .Columns.Count
Range(Cells(1, 1), Cells(theRows, theColumns)).Select
End With
Selection.Copy
Set AlphaExportBook = ActiveWorkbook
End Sub
Private Sub OpenNewWksheet()
'******************************
' Open the File Dialog
'******************************
Dim ReversionWBook As Workbook
With Application.FileDialog(msoFileDialogOpen)
.AllowMultiSelect = False
.Show
.Execute
If (.SelectedItems.Count = 0) Then
MsgBox "User Cancelled Operation"
' GoTo EndofInstructions
Else
End If
End With
ActiveWorkbook.Activate
Set ReversionWBook = ActiveWorkbook
End Sub
Private Sub CloseWkbook(AlphaExportBook As Workbook)
'**********************************
' Close Alpha Export WorkBook
'**********************************
AlphaExportBook.Activate
Application.DisplayAlerts = False
AlphaExportBook.Close SaveChanges:=False
Application.DisplayAlerts = True
End Sub

Firstly, you shouldn't be getting an "Argument not optional" error when calling OpenNewWksheet because that subroutine is not expecting arguments. You would get that error trying to call CloseWkbook without specifying a parameter, because that subroutine expects a Workbook object to be passed to it.
The easiest way to make the workbook available to all subroutines is to declare the variable with module-level scope, e.g.
Option Explicit
Dim AlphaExportBook As Workbook
Sub TestSharedVars()
CopyCellsthenClose
OpenNewWksheet
CloseWkbook
End Sub
Private Sub CopyCellsthenClose()
Dim theRows
Dim theColumns
With ActiveSheet.UsedRange
theRows = .Rows.Count
theColumns = .Columns.Count
'Note - the following line won't do what you expect unless
' UsedRange starts at cell A1
Range(Cells(1, 1), Cells(theRows, theColumns)).Select
End With
Selection.Copy
Set AlphaExportBook = ActiveWorkbook
End Sub
Private Sub OpenNewWksheet()
'******************************
' Open the File Dialog
'******************************
Dim ReversionWBook As Workbook ' Does this need to be module-level scope too?
With Application.FileDialog(msoFileDialogOpen)
.AllowMultiSelect = False
.Show
.Execute
If .SelectedItems.Count = 0 Then
MsgBox "User Cancelled Operation"
End If
End With
'ActiveWorkbook.Activate ' This is redundant - the ActiveWorkbook is already active
Set ReversionWBook = ActiveWorkbook
End Sub
Private Sub CloseWkbook()
'**********************************
' Close Alpha Export WorkBook
'**********************************
'You don't need to activate the workbook before you close it
'AlphaExportBook.Activate
Application.DisplayAlerts = False
AlphaExportBook.Close SaveChanges:=False
Application.DisplayAlerts = True
End Sub
Alternatively, you could pass the workbook object between subroutines as follows:
Option Explicit
Sub TestSharedVars()
'Dimension object to have scope only within this subroutine, but we
' will pass a reference to this object to the other subroutines that
' need to reference it
Dim AlphaExportBook As Workbook
CopyCellsthenClose AlphaExportBook
OpenNewWksheet
CloseWkbook AlphaExportBook
End Sub
Private Sub CopyCellsthenClose(wb As Workbook)
Dim theRows
Dim theColumns
With ActiveSheet.UsedRange
theRows = .Rows.Count
theColumns = .Columns.Count
'Note - the following line won't do what you expect unless
' UsedRange starts at cell A1
Range(Cells(1, 1), Cells(theRows, theColumns)).Select
End With
Selection.Copy
Set wb = ActiveWorkbook
End Sub
Private Sub OpenNewWksheet()
'******************************
' Open the File Dialog
'******************************
Dim ReversionWBook As Workbook ' Does this need to be module-level scope too?
With Application.FileDialog(msoFileDialogOpen)
.AllowMultiSelect = False
.Show
.Execute
If .SelectedItems.Count = 0 Then
MsgBox "User Cancelled Operation"
End If
End With
'ActiveWorkbook.Activate ' This is redundant - the ActiveWorkbook is already active
Set ReversionWBook = ActiveWorkbook
End Sub
Private Sub CloseWkbook(wb As Workbook)
'**********************************
' Close Alpha Export WorkBook
'**********************************
'You don't need to activate the workbook before you close it
'wb.Activate
Application.DisplayAlerts = False
wb.Close SaveChanges:=False
Application.DisplayAlerts = True
End Sub

Related

Use the same workbook in multiple modules

I have a workbook with two buttons. So an user can press them to open two workbooks (Input and Output). Then he will copy data from Input to Output. My problem here is that I don't know how to define the same workbook for my subs.
This is the code for opening the files:
Public wb1 As Workbook
Public wb2 As Workbook
Public result As Integer
Public fDialog As FileDialog
Public Sub inputs()
Set fDialog = Application.FileDialog(msoFileDialogOpen)
Set control = Workbooks("Control.xlsm").Worksheets("Control")
fDialog.Title = "Select a file"
fDialog.Title = "Select a file"
If fDialog.Show = -1 Then
If Right(fDialog.SelectedItems(1), 5) = ".xlsx" Or Right(fDialog.SelectedItems(1), 4) = ".xls" Then
Set wb1 = Workbooks.Open(fDialog.SelectedItems(1))
control.Cells(6, 2) = fDialog.SelectedItems(1)
Else
MsgBox ("Please select an excel file")
Exit Sub
End If
End If
End Sub
This is the other sub for copying data:
Public Sub stack()
For Each ws In wb1.Worksheets
'---here is the code for copying data---
Exit For
Exit Sub
When I run this code, of course it gives me this error "object variable or With block not set".
Do you know how can I resolve this? How can I use the same wb1 for both subs?
It's better to limit the scope of your variable whenever possible so what you can do is this:
Public Sub stack()
Dim wbControl As Worksheet
Set wbControl = Workbooks("Control.xlsm").Worksheets("Control")
'Check if there is possible input path in cell B6
If wbControl.Cells(6, 2).Value2 = vbNullString Then
MsgBox "Provide the Input workbook path first!"
Exit Sub
End If
'Check if there is possible output path in cell B6
If wbControl.Cells(6, 2).Value2 = vbNullString Then
MsgBox "Provide the Output workbook path first!"
Exit Sub
End If
'More error checking - e.g. check if both path are valid (file exist?)
Dim wbInput As Workbook
Set wbInput = Workbooks.Open(wbControl.Cells(6, 2).Value2) 'Input path in cell B6
Dim wbOutput As Workbook
Set wbOutput = Workbooks.Open(wbControl.Cells(6, 5).Value2) 'Output path in cell E6
Dim ws As Worksheet
For Each ws In wbInput.Worksheets
'Do whatever you want in here
Next ws
'Remember to close the workbook if not needed later
End Sub
Remove this line from your inputs sub (and also similar one for selecting the output file)
Set wb1 = Workbooks.Open(fDialog.SelectedItems(1))
Please, use the next way:
Declare public variables for both necessary workbooks, on top of the standard module:
Public wb1 As Workbook, wb2 As Workbook
Copy the next sub in a standard module, to open and Set the necessary workbooks:
Sub SetWorbooks()
Dim fdialog As FileDialog, i As Long
Set fdialog = Application.FileDialog(msoFileDialogFilePicker)
For i = 1 To 2
fdialog.Title = "Please, select """ & IIf(i = 1, "Input", "Output") & """ file"
If fdialog.Show = -1 Then
If Right(fdialog.SelectedItems(1), 5) = ".xlsx" Or Right(fdialog.SelectedItems(1), 4) = ".xls" Then
If i = 1 Then
Set wb1 = Workbooks.Open(fdialog.SelectedItems(1))
Else
Set wb2 = Workbooks.Open(fdialog.SelectedItems(1))
End If
Else
MsgBox "Please select an excel file (xls, xlsx)!"
Exit Sub
End If
Else
MsgBox "You should select an Excel file...": Exit Sub
End If
Next i
End Sub
Then use them in this way:
Sub Mystack()
Dim ws As Worksheet
If wb1 Is Nothing Then SetWorbooks 'for cases when having an error and the wb(s) reference have been lost.
For Each ws In wb1.Worksheets
'---here is the code for copying data---
Next
End Sub

Copy data from different workbook and paste it to specific sheets on a report workbook

Currently i am new on studying VBA for reporting and im still learning from it. moving on, may i ask a help on this one? :), my scenario is this.
i have data on 20 workbooks (POLY, BAYO, PROPO, TIPAS, CITRO....etc) with sheet name (Sheet1)
i have a single workbook for summary with many sheets, its sheet name is based on 20 workbook file name but not in alphabetical order. (Sheet name = CITRO, BAYO, PROPO, POLY, TIPAS....etc)
i want to copy the data on each workbook and paste it to their respective sheet name based on file name and specific cell ("B2:F2")
is it doable?
here's the code im trying to work on, the problem is, it is creating its own sheet instead of pasting it to my desire sheet.
Private Sub CommandButton1_Click()
Dim SourceBook As Workbook
Dim CurrentBook As Workbook
application.screenupdating = false
Set CurrentBook = ThisWorkbook
Set SourceBook = Workbooks.Open("C:\CITRO.xlsx")
SourceBook.Sheets("Sheet1").Range("B2:F2").Copy Destination:=CurrentBook.Sheets("CITRO").Range("R2:V2")
Set SourceBook = Workbooks.Open("C:\BAYO.xlsx")
SourceBook.Sheets("Sheet1").Range("B2:F2").Copy Destination:=CurrentBook.Sheets("BAYO").Range("R2:V2")
Set SourceBook = Workbooks.Open("C:\PROPO.xlsx")
SourceBook.Sheets("Sheet1").Range("B2:F2").Copy Destination:=CurrentBook.Sheets("PROPO").Range("R2:V2")
MsgBox "Completed"
Application.Workbooks("CITRO").Close
Application.Workbooks("BAYO").Close
Application.Workbooks("PROPO").Close
'SourceBook.Close
'Set SourceBook = Nothing
'Set CurrentBook = Nothing
'ThisWorkbook.Activate
'Application.Worksheets("Summary").Activate
'Application.Worksheets("Summary").Range("B2:F2").Select
End Sub
You need to close the SourceBook before opening a new one with SourceBook.Close SaveChanges:=False
Private Sub CommandButton1_Click()
Dim SourceBook As Workbook
Dim CurrentBook As Workbook
Application.ScreenUpdating = False 'don't forget to activate it in the end
Set CurrentBook = ThisWorkbook
Set SourceBook = Workbooks.Open("C:\CITRO.xlsx")
SourceBook.Worksheets("Sheet1").Range("B2:F2").Copy Destination:=CurrentBook.Worksheets("CITRO").Range("R2:V2")
SourceBook.Close SaveChanges:=False
Set SourceBook = Workbooks.Open("C:\BAYO.xlsx")
SourceBook.Worksheets("Sheet1").Range("B2:F2").Copy Destination:=CurrentBook.Worksheets("BAYO").Range("R2:V2")
SourceBook.Close SaveChanges:=False
Set SourceBook = Workbooks.Open("C:\PROPO.xlsx")
SourceBook.Worksheets("Sheet1").Range("B2:F2").Copy Destination:=CurrentBook.Worksheets("PROPO").Range("R2:V2")
SourceBook.Close SaveChanges:=False
Application.ScreenUpdating = True
MsgBox "Completed"
End Sub
Alternatively you can use a procedure to shorten it:
Private Sub CommandButton1_Click()
Application.ScreenUpdating = False 'don't forget to activate it in the end
CopyIntoThisWorkbook "C:\CITRO.xlsx", "CITRO"
CopyIntoThisWorkbook "C:\BAYO.xlsx", "BAYO"
CopyIntoThisWorkbook "C:\PROPO.xlsx", "PROPO"
Application.ScreenUpdating = True
MsgBox "Completed"
End Sub
Private Sub CopyIntoThisWorkbook(ByVal SourceFileName As String, ByVal DestinationSheetName As Range)
Dim SourceBook As Workbook
Set SourceBook = Workbooks.Open(SourceFileName)
SourceBook.Worksheets("Sheet1").Range("B2:F2").Copy Destination:=ThisWorkbook.Worksheets(DestinationSheetName).Range("R2:V2")
SourceBook.Close SaveChanges:=False
End Sub
And if the sheet name CITRO is always the filename CITRO.xlsx then you can even use an array with a loop:
Private Sub CommandButton1_Click()
Application.ScreenUpdating = False 'don't forget to activate it in the end
Dim SheetNameList() As Variant
SheetNameList = Array("CITRO", "BAYO", "PROPO") 'easily extendable
Dim SheetName As Variant
For Each SheetName In SheetNameList
CopyIntoThisWorkbook SheetName
Next SheetName
Application.ScreenUpdating = True
MsgBox "Completed"
End Sub
Private Sub CopyIntoThisWorkbook(ByVal DestinationSheetName As String)
Dim SourceBook As Workbook
Set SourceBook = Workbooks.Open("C:\" & DestinationSheetName & ".xlsx")
SourceBook.Worksheets("Sheet1").Range("B2:F2").Copy Destination:=ThisWorkbook.Worksheets(DestinationSheetName).Range("R2:V2")
SourceBook.Close SaveChanges:=False
End Sub

Import a worksheet from another workbook (#2) to current workbook (#1)

I wrote a code that is opening a window in which I can select a the excel workbook (#2) I want to copy and import the worksheet from.
The Code is then checking whether the wanted worksheet (named "Guidance") exists in the opened workbook (#2).If so it should be copied and pasted into the current workbook (#1).
After pasting the worksheet the workbook (#2) should be closed again.
So far the code does what I want it to do, as it opens the window and lets me select the wanted worksheet (named "Guidance") but I have the bug (not sure if the translation is correct)
"Runtime error '9': index out of range"
where the worksheet is supposed to be copied and pasted.
Any help on that would be very much appreciated! Thanks in advance.
Private Function SheetExists(sWSName As String, Optional InWorkbook As Workbook) As Boolean
If InWorkbook Is Nothing Then
Set InWorkbook = ThisWorkbook
End If
Dim ws As Worksheet
On Error Resume Next
Set ws = Worksheets(sWSName)
If Not ws Is Nothing Then SheetExists = True
On Error GoTo 0
End Function
Sub GuidanceImportieren()
Dim sImportFile As String, sFile As String
Dim sThisWB As Workbook
Dim vFilename As Variant
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Set sThisWB = ActiveWorkbook
sImportFile = Application.GetOpenFilename("Microsoft Excel Workbooks,
*xls; *xlsx; *xlsm")
If sImportFile = "False" Then
MsgBox ("No File Selected")
Exit Sub
Else
vFilename = Split(sImportFile, "|")
sFile = vFilename(UBound(vFilename))
Application.Workbooks.Open (sImportFile)
Set wbWB = Workbooks("sImportFile")
With wbWB
If SheetExists("Guidance") Then
Set wsSht = .Sheets("Guidance")
wsSht.Copy Before:=sThisWB.Sheets("Guidance")
Else
MsgBox ("No worksheet named Guidance")
End If
wbWB.Close SaveChanges:=False
End With
End If
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
The issue is here
Set wbWB = Worksheets("Guidance") '<-- this should be a workbook not a worksheet?
With wbWB '<-- this with is useless until …
If SheetExists("Guidance") Then
Set wsSht = .Sheets("Guidance") '<-- … until Sheets here starts with a dot
wsSht.Copy Before:=sThisWB.Sheets("Guidance") 'if the error is here then there is no sheet "Guidance" in sThisWB
Else
MsgBox ("No worksheet named Guidance")
End If
wbWB.Close SaveChanges:=False
End With
Also note that SheetExists("Guidance") does not check in a specific workbook (which may fail). I recommend to extend the function to:
Private Function SheetExists(WorksheetName As String, Optional InWorkbook As Workbook) As Boolean
If InWorkbook Is Nothing Then
Set InWorkbook = ThisWorkbook 'fallback if not set
End If
Dim ws As Worksheet
On Error Resume Next
Set ws = InWorkbook.Worksheets(WorksheetName)
SheetExists = Not ws Is Nothing
On Error Goto 0 'necessary because the Err.Number will not be cleared on End Function
End Function
So you can test if a worksheet exists in a specific workbook like
SheetExists("Guidance", sThisWB)
SheetExists("Guidance", wbWB)
Sub GuidanceImportieren()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim sImportFile As String
sImportFile = Application.GetOpenFilename("Microsoft Excel Workbooks, *xls; *xlsx; *xlsm")
If sImportFile = False Then 'false should not be "false"
MsgBox "No File Selected"
Exit Sub
Else
Dim vFilename As Variant
vFilename = Split(sImportFile, "|")
Dim sFile As String
sFile = vFilename(UBound(vFilename))
Dim ImportWorkbook As Workbook
Set ImportWorkbook = Application.Workbooks.Open(sImportFile)
If SheetExists("Guidance", ImportWorkbook) Then
ImportWorkbook.Sheets("Guidance").Copy Before:=ThisWorkbook.Sheets("Guidance")
'you might need to change it into something like this:
Else
MsgBox "No worksheet named Guidance"
End If
ImportWorkbook.Close SaveChanges:=False
End If
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub

Copy data from current workbook and paste it to another open workbook with userform list

I am wanting to have a button open a userform with a list of all open Workbooks. The user selects the workbook they want and the code copies data from a fixed range in the current workbook and pastes it into a fixed range in the user selected workbook.
While searching around I found this code, that works similarly but copies from the selected workbook and pastes into the current one.
Option Explicit
Const PSWD = "atari"
Private Sub CancelButton_Click()
Unload Me
End Sub
Private Sub CopyPasteButton_Click()
ActiveSheet.Unprotect Password:=PSWD
'This code will be executed when the "Copy" button is clicked on the userform.
Dim wsData As Worksheet
Dim rCopy As Range
Dim CopyRw As Long
Set wsData = ThisWorkbook.Sheets("SALES Details")
With Application
.DisplayAlerts = False
.ScreenUpdating = True
With wsData
.Unprotect PSWD
CopyRw = .Cells(.Rows.Count, 1).End(xlUp).Row + 1
End With
On Error GoTo exit_err
With Workbooks(Me.ListBox1.Value).Sheets("Master Sheet")
Set rCopy = .Cells(10, 1).CurrentRegion
Set rCopy = rCopy.Offset(1, 0).Resize(rCopy.Rows.Count - 1, 40)
rCopy.Copy ThisWorkbook.Sheets("SALES Details").Cells(CopyRw, 1)
End With
Unload Me
exit_err:
wsData.Protect Password:=PSWD
.DisplayAlerts = True
.ScreenUpdating = True
.CutCopyMode = False
End With
End Sub
Private Sub UserForm_Activate()
'Populate list box with names of open workbooks, excluding main workbook.
Dim wb As Workbook
For Each wb In Workbooks
If wb.Name <> ThisWorkbook.Name Then ListBox1.AddItem wb.Name
Next wb
End Sub
This code works great, for what it does. I have been trying to edit it without luck. How can I edit this to reverse the direction and have it copy from a fixed range in the current sheet (A50:J57) to a fixed range on the user selected sheet (A4:J11)?
I think this should work. Of course you have to adapt the sheet names in code.
Private Sub CopyPasteButton_Click()
Dim mySheet As Worksheet, otherSheet As Worksheet
On Error GoTo exit_err
Application.DisplayAlerts = False
Set mySheet = ThisWorkbook.Sheets("SheetXYZ")
Set otherSheet = Workbooks(Me.ListBox1.Value).Sheets("SheetABC")
mySheet.Range("A50:J57").Copy Destination:=otherSheet.Range("A4:J11")
exit_err:
Application.DisplayAlerts = True
End Sub
UPDATE
For copying the values and not the formulas of the range use this code instead of the Copy function:
mySheet.Range("A50:J57").Copy
otherSheet.Range("A4:J11").PasteSpecial xlPasteValuesAndNumberFormats
For further options of the PasteSpecial function see the documentation.

Adding worksheets on workbook_open

I have an existing worksheet "StudentSheet1" which I need to add as many times as a user needs.
For eg, if a user enters 3 in cell "A1", saves it and closes the workbook.
I want to have three sheets: "StudentSheet1" , "StudentSheet2" and "StudentSheet3" when the workbook is opened next time.
So I will have the Code in "Workbook_Open" event. I know how to insert new sheets, but cant insert this particular sheet "StudentSheet1" three times
Here is my code:
Private Sub Workbook_Open()
Application.ScreenUpdating = False
Dim ws As Worksheet
Set ws = ActiveWorkbook.Sheets.Add(Type:=xlWorksheet, After:=Worksheets(1))
Application.ScreenUpdating = True
End Sub
EDIT
Sorry I misread the question, try this:
Private Sub Workbook_Open()
Dim iLoop As Integer
Dim wbTemp As Workbook
If Not Sheet1.Range("A1").value > 0 Then Exit Sub
Application.ScreenUpdating = False
Set wbTemp = Workbooks.Open(Filename:="//Ndrive/Student/Student.xlsm")
wbTemp.Sheets("StudentSheet1").Copy After:=ThisWorkbook.Sheets(Sheets.Count)
wbTemp.Close
Set wbTemp = Nothing
With Sheet1.Range("A1")
For iLoop = 2 To .Value
Sheets("StudentSheet1").Copy After:=ThisWorkbook.Sheets(Sheets.Count)
ActiveSheet.Name = "StudentSheet" & iLoop
Next iLoop
.Value = 0
End With
Application.ScreenUpdating = True
End Sub
Why are you wanting to add sheets on the workbook open? If the user disables macros then no sheets will be added. As Tony mentioned, why not add the sheets when called by the user?
EDIT
As per #Sidd's comments, if you need to check if the sheet exists first use this function:
Function SheetExists(sName As String) As Boolean
On Error Resume Next
SheetExists = (Sheets(sName).Name = sName)
End Function
user793468, I would recommend a different approach. :)
wbTemp.Sheets("StudentSheet1").Copy After:=ThisWorkbook.Sheets(Sheets.Count)
is not reliable. Please see this link.
EDIT: The above code will fail if the workbook has defined names. Otherwise it is absolutely reliable. Thanks to Reafidy for catching that.
I just noticed OP's comment about the shared drive. Adding amended code to incorporate OP's request.
Tried and Tested
Option Explicit
Const FilePath As String = "//Ndrive/Student/Student.xlsm"
Private Sub Workbook_Open()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim TempName As String, NewName As String
Dim ShtNo As Long, i As Long
On Error GoTo Whoa
Application.ScreenUpdating = False
Set wb1 = ActiveWorkbook
Set ws1 = wb1.Sheets("Sheet1")
ShtNo = ws1.Range("A1")
If Not ShtNo > 0 Then Exit Sub
Set wb2 = Workbooks.Open(FilePath)
Set ws2 = wb2.Sheets("StudentSheet1")
For i = 1 To ShtNo
TempName = ActiveSheet.Name
NewName = "StudentSheet" & i
If Not SheetExists(NewName) Then
ws2.Copy After:=wb1.Sheets(Sheets.Count)
ActiveSheet.Name = NewName
End If
Next i
'~~> I leave this at your discretion.
ws1.Range("A1").ClearContents
LetsContinue:
Application.ScreenUpdating = True
On Error Resume Next
wb2.Close savechanges:=False
Set ws1 = Nothing
Set ws2 = Nothing
Set wb2 = Nothing
Set wb1 = Nothing
On Error GoTo 0
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
'~~> Function to check if sheet exists
Function SheetExists(wst As String) As Boolean
Dim oSheet As Worksheet
On Error Resume Next
Set oSheet = Sheets(wst)
On Error GoTo 0
If Not oSheet Is Nothing Then SheetExists = True
End Function

Resources