excel VBA passing values through subs compile error - excel

I'm new to vba and I'm trying to make an excel macro that loops through a selected folder and the file tabs. It will then call another sub to copypaste certain cells. I got a compile error(procedure declaration does not match description of event or procedure having the same name) when trying to pass through the values.
Part of the first sub
Set wb = Workbooks.Open(Filename:=myPath & myFile)
For Each sht In ActiveWorkbook.Sheets
CommandButton5_Click strSourceSheet, strDestinationSheet
Next sht
Second sub
Sub CommandButton5_Click(strSourceSheet As String, strDestinationSheet As String)
Dim strSourceSheet As String, strDestinationSheet As String, sourceData As String
strSourceSheet = "Sheet1"
strDestinationSheet = "Sheet1"
Sheets(strDestinationSheet).Cells(1, "A") = Sheets(strSourceSheet).Cells(2, "A").Value
End Sub

In Excel, the "tabs" are called sheets. (or worksheets)
Folders (or directories) are file related and are completely different.
as per Comintern, I'd suggest that you rename sub 2 to something like this (give it a meaningful name):
CopySelectedCells(strSourceSheet As String, strDestinationSheet As String)
and then call that from sub 1.
If you have a button called CommandButton5 then you probably need to have a click event for it. Create a third sub:
Sub CommandButton5_Click()
End Sub
Note on parameters:
you'll get further with your code if you set your parameters in sub 1 before you call sub 2
good luck.

Related

VBA Application.Run for Running Macros-with-Parameters Stored Inside an Excel Cell

I have stored a few macro names inside Excel cells.
I loop through those cells and call macros written inside them as follows.
[The subroutine below is called from another subroutine by providing the parameters correctly.]
Sub SelectAppsToRun(ctlGrpName As String, ws As Worksheet, activeTbx As MSForms.TextBox)
Dim rng as Range
For each rng in Sheet1.Range(“A1:A5”)
Application.Run rng.value
Next rng
End Sub
It works correctly when there are no parameters for the macros. But generates error when it tries to run the following macro stored in a cell.
JumpToNextCtl, ws, ctlGrpName, activeTbx
This macro is supposed to take its parameters - ws, ctlGrpName and activeTbx - from the subroutine 'SelectAppsToRun'
The codes it is supposed to run is:
Sub JumpToNextCtl(ws As Worksheet, ctlGrpName As String, Optional activeTbx As MSForms.TextBox, Optional activeCbx As MSForms.ComboBox, Optional chkBx As MSForms.CheckBox)
Dim shp As Shape, i As Integer, ctlname As String
Dim ctlColl As New Collection
For Each shp In ws.Shapes.Range(ctlGrpName).GroupItems
If shp.Type = msoOLEControlObject Then
If TypeName(shp.OLEFormat.Object.Object) = "TextBox" Or _
TypeName(shp.OLEFormat.Object.Object) = "ComboBox" Or _
TypeName(shp.OLEFormat.Object.Object) = "CheckBox" Then
ctlColl.Add shp.OLEFormat.Object
End If
End If
Next sh
End Sub
The error message I get is:
Cannot run the macro '"JumpToNextCtl", ws, ctlGrpName, activeTbx'. The macro may not be available in this workbook or all macros may be disabled.
I guess Application.Run takes all the parameters as String. Is there anyway to get this method running?
Please, try using the next scenario:
The main Sub which calls "SelectAppsToRun":
Sub MainCall()
SelectAppsToRun "This is a call. It is ", ActiveSheet, ActiveWorkbook
SelectAppsToRun "This is a call. It should be ", ActiveSheet, , True
End Sub
A test "SelectAppsToRun" Sub may look like this:
Sub SelectAppsToRun(ctlGrpName As String, ws As Worksheet, Optional wb As Workbook, Optional boolOK As Boolean)
Dim arrProc, El
arrProc = Split("testCall1,testCall2", ",") 'this may be considered the equivalent of cell range values
For Each El In arrProc
Application.Run El, ctlGrpName, ws, wb, boolOK
Next
End Sub
The following Subs to be called (with object parameters) from "SelectAppsToRun":
Sub testCall1(strTest, sh As Worksheet, Optional wb As Workbook, Optional boolOK As Boolean)
Debug.Print strTest & " It is the first", "The sheet name is " & sh.name
If Not wb Is Nothing Then Debug.Print wb.Path
If boolOK Then Debug.Print "The situation looks good..."
End Sub
Sub testCall2(strTest, sh As Worksheet, Optional wb As Workbook, Optional boolOK As Boolean)
Debug.Print strTest & " It is the second", "Range ""A1"" value is " & sh.Range("A1").Value2
If Not wb Is Nothing Then Debug.Print wb.fullName
If boolOK Then Debug.Print "The situation is good..."
End Sub
Open Immediate Window (Ctrl + G, being in VBE), click inside "MainCall" Sub and run the code line by line, pressing F8.
This Sub will call SelectAppsToRun, passing three parameters (one string and two objects). Then each of the two test Subs (testCall1 and testCall2) will be called with the three parameters.
Isn't it what you need achieving? If yes, glad I could help. If not, please describe where this scenario does not match your need.

How can I add sheets from an excel file to another?

So I am trying to write a Macro for Excel, that adds 2 worksheets from an excel file to a new one.
Therefore, I try this:
Sub addfile()
Dim sheet1 As Worksheet
Dim sheet2 As Worksheet
Set sheet1 = Sheets.Add(Type:="C:\Users\Helge\AppData\Roaming\Microsoft\Templates\page1.xltx")
Set sheet2 = Sheets.Add(Type:="C:\Users\Helge\AppData\Roaming\Microsoft\Templates\page2.xltx")
End Sub
When I test it, it imports the first page, but the 2nd page gives me a Runtime error 1004.
Why does this happen?
And is there another way to get 2 sheets from one excel file to another via vba?
Much to my surprise this version of your code actually worked for me.
Sub addfile()
Dim Sheet1 As Worksheet
Dim Sheet2 As Worksheet
Set Sheet1 = Sheets.Add(Type:=Environ("Userprofile") & "\OneDrive\Desktop\Template1.xltx")
Set Sheet2 = Sheets.Add(Type:=Environ("Userprofile") & "\OneDrive\Desktop\Book2.xlsx")
Debug.Print Sheet1.Name, Sheet2.Name
End Sub
The reason for my surprise is that Sheet1 and Sheet2 are the default CodeName for the first and second worksheets in any workbook. Therefore there is a conflict of naming between the Sheet1 in the workbook and the Sheet1 you declare which should come to the surface not later than Debug.Print Sheet1.Name. In fact, it may have. I didn't check which name was printed. But the code didn't crash. Since it crashes on your computer, perhaps you have an older version of Excel. Try to stay clear of variable names that Excel also uses. Or there is something wrong with the path & file name, which is hard to tell in that syntax and therefore kept me fooled for quite some time too.
In fact, I discovered the above only after finding out that my Desktop was on OneDrive and not before I had written the function below which is designed to avoid the use of Sheets.Add. It also has some extras such as being able to specify the sheet to take from the template (you could have one template with 2 or more sheets). You can specify an index number or a sheet name. And the function will give a name to the copy, too, if you specify one.
Private Function AddWorksheet(ByVal Template As String, _
TabId As Variant, _
Optional ByVal TabName As String) As Worksheet
Dim Wb As Workbook
Dim Path As String
Dim FileName As String
Set Wb = ThisWorkbook ' change to suit
' make sure the path ends on "\"
Path = "C:\Users\Helge\AppData\Roaming\Microsoft\Templates\"
With Workbooks.Open(Path & Template)
.Sheets(TabId).Copy After:=Wb.Sheets(Wb.Sheets.Count)
.Close
End With
Set AddWorksheet = ActiveSheet
If Len(TabName) Then ActiveSheet.Name = TabName
End Function
You can call the function from a sub routine like this:-
Sub AddWorksheets()
Dim Tab1 As Worksheet
Dim Tab2 As Worksheet
Application.ScreenUpdating = False
Set Tab1 = AddWorksheet("Page1.xltx", 1, "New Tab")
Set Tab2 = AddWorksheet("Page2.xltx", "Sheet1", "Another new Tab")
Application.ScreenUpdating = True
End Sub
Please observe the difference between the two function calls.

Check if Workbook variable has been set

I have a sub with an optional workbook argument. I want an If to check whether that variable was passed, and if not set the variable to the active worksheet.
Sub SaveWorkbook(NewPathName As String, Optional Workbook As Workbook)
'This is very simplified, the real thing has various other parameters
If IsNull(Workbook) Then Set Workbook = ActiveWorkbook
'Then loads more stuff
End Sub
Things I have tried include:
IsNull(Workbook)
IsEmpty(Workbook)
Workbook = Nothing
None trigger the If statement so the code attempts to continue with Workbook set to Empty, and then hits errors.
Do not use the word "Workbook" as the variable name. Try it like this:
Sub SaveWorkbook(NewPathName As String, Optional wb As Workbook)
If wb Is Nothing Then
MsgBox "workbook not set"
Set wb = ActiveWorkbook
End If
MsgBox wb.Name
End Sub
In VBA the isMissing function only works, if you declare the parameter as Variant. See in the description
This should work:
Sub SaveWorkbook(NewPathName As String, Optional Workbook As Variant)
'This is very simplified, the real thing has various other parameters
If isMissing(Workbook) Then Set Workbook = ActiveWorkbook
'Then loads more stuff
End Sub

Can you call a module from a userform whose variable is declared from a combo box?

I created a userform sub that will allow the user to choose from the open workbook to use as the reference workbook using a combo box. My current script is returning an error that workbooks(wb) is not defined - I assume this is because the variable is defined in two different modules and the combo box is not in the module being called. Ideally would like to use the userform below
Private Sub Go_Click()
If ComboBox1.ListIndex = -1 Then
MsgBox "Please select a workbook name and try again"
Exit Sub
End If
Dim wb As Variant
wb = ComboBox1.List(ComboBox1.ListIndex)
Call GenerateReportUserForm
End Sub
To call this sub:
Sub newMacro()
Dim copyNames As Range, pasteNames As Range, copyAmounts As Range, pasteAmounts As Range, copyDates As Range, pasteDates As Range, _
copyPayment As Range, pastePayment As Range
' For cheques only
Set copyNames = Workbooks(wb).Worksheets(2).Columns("F")
Set copyAmounts = Workbooks(wb).Worksheets(2).Columns("AR")
Set copyDates = Workbooks(wb).Worksheets(2).Columns("AI")
Set copyPayment = Workbooks(wb).Worksheets(2).Columns("AJ")
Set pasteNames = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("A")
Set pasteAmounts = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("C")
Set pasteDates = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("D")
Set pastePayment = Workbooks("VBA Workbook.xlsm").Worksheets(1).Columns("E")
copyNames.Copy Destination:=pasteNames
copyAmounts.Copy Destination:=pasteAmounts
copyDates.Copy Destination:=pasteDates
copyPayment.Copy Destination:=pastePayment
End sub
Thanks!
wb clearly wants to be a String representing the name of a workbook. Declare it as such.
Dim wbName As String
wbName = ComboBox1.List(ComboBox1.ListIndex)
Side note, name your controls. AvailableFilesBox tells so much more than ComboBox1.
Now, what you want is to pass this variable as an argument; do not use a global variable unless you absolutely MUST.
Call GenerateReportUserForm
Not sure what this is supposed to be doing, but it's not calling newMacro. If you want to make it call newMacro, then change it to this:
NewMacro wbName
Or if you really really want to keep that redundant and distracting Call keyword:
Call NewMacro(wbName)
Note: give that macro a meaningful name that describes what the macro does. "new macro" might be clear now, but not so much once there are 4-5 newer macros in that project - and newMacro2 is NOT an option!
Now, in order to pass wbName as an argument, the procedure needs to declare that it takes a parameter - like this:
Public Sub NewMacro(ByVal wbName As String)
Inside that procedure scope, you don't need to constantly dereference the Workbook object. Do it once, store the object reference into a local variable, then use that variable:
Dim wb As Workbook
Set wb = Workbooks(wbName)
Turns out, that macro doesn't really care for the workbook's name; what it really actually wants is a Workbook object. So, let's make it the caller's responsibility to provide a Workbook.
First we change the signature to take a Workbook parameter:
Public Sub NewMacro(ByVal wb As Workbook)
Then we change the form code to supply it:
Dim wb As Workbook
Set wb = Workbooks(ComboBox1.List(ComboBox1.ListIndex))
NewMacro wb ' or: Call NewMacro(wb)
Remember to always put Option Explicit at the top of every module; Rubberduck can help you find & fix this, and other issues in your code.

VBA Add second Sheet with same Name

I have a CommandButton which opens a UserForm and create a copied Sheet with the name of the ComboBox Value.
This is My Code:
Private Sub CommandButton1_Click()
[UserForm1].Show ' Open UserForm
End Sub
Private Sub CommandButton2_Click()
Dim ws As Worksheet
ActiveWorkbook.Sheets("Sheet1").Visible = True ' Unhide Sheet
Sheets("Sheet1").Copy _
Before:=ActiveWorkbook.Sheets("Sheet1") ' Copy Sheet
Set ws = ActiveSheet
ws.Name = ComboBox1.Value ' Name Sheet
[UserForm1].Hide ' Close UserForm
ActiveWorkbook.Sheets("Sheet1").Visible = False ' Hide Sheet again
End sub
Now my problem is, if there are two machines with name "Machine Type 1" Excel gets an Error. So what do i have to change in my code, that the second sheet would named e.g. "Machine Type 1 (2)?
Thanks for your help.
you could try this
Private Sub CommandButton1_Click()
If IsSheetThere(ComboBox1.Value) Then 'if some sheet with chosen name already there
Sheets(ComboBox1.Value).Copy Before:=Sheets(10) ' copy the existing sheet
With ActiveSheet 'reference just copied sheet
.UsedRange.Clear 'clear its content
Sheets("Sheet1").UsedRange.Copy ActiveSheet.Range("A1") ' copy Sheet1 content and paste into it
End With
Else 'otherwise
Sheets("Sheet1").Copy Before:=Sheets(Sheets.Count) ' make a copy of "Sheet1" sheet
ActiveSheet.Name = ComboBox1.Value 'and rename it as per chosen name
End If
Me.Hide
End Sub
Function IsSheetThere(shtName As String) As Boolean
On Error Resume Next
IsSheetThere = Not Sheets(shtName) Is Nothing
End Function
the code line:
Sheets(ComboBox1.Value).Copy Before:=Sheets(10) ' copy the existing sheet
is the one that leaves Excel the burden of somehow "counting" the number of already existing sheets with the chosen name, and name the new one appropriately
You can use the following sub which calls the below function, just apply the same logic using .Copy
Sub create_new_sheet_with_name(name As String, wb As Workbook, aftersheet As Variant)
Dim i As Integer
i = 2
If sheet_name_exists(name, wb) Then
Do While sheet_name_exists(name & " (" & i & ")", wb)
i = i + 1
Loop
wb.Sheets.Add(after:=aftersheet).name = name & " (" & i & ")"
Else
wb.Sheets.Add(after:=aftersheet).name = name
End If
End Sub
Function sheet_name_exists(name As String, wb As Workbook) As Boolean
For Each sheet In wb.Worksheets
If sheet.name = name Then
sheet_name_exists = True
Exit Function
End If
Next sheet
sheet_name_exists = False
End Function
here's an example of how to use the sub:
Sub test()
create_new_sheet_with_name "hi", ThisWorkbook, ThisWorkbook.Sheets(1)
'this adds a new sheet named "hi" to thisworkbook after thisworkbook.sheets(1)
End Sub
Technically this isn't an answer to this question... but it's better because it will help you solve this and many other coding tasks on your own.
There is a simple way to create VBA code for most basic tasks.
If there's something Excel can do that you want to be able to do programmatically, just Record a Macro of yourself performing the action(s), and then look at the code that Excel generated.
I have a terrible memory, I can't remember commands I used yesterday. So it's not only quicker and less frustrating for others for me to figure it out myself, but the more often I do that, the quicker I'll learn (without asking others to do the thinking for me on a basic question).
I fact, I'm guess that the majority of veteran VBA coders learned at least partly by analyzing recorded macros. I know I did.

Resources