Can one create generic macro that works on multiple worksheets? - vba

I have created a simple worksheet macro that sends the contents of a cell to an external program when the cell is double clicked. At first, I had all of the code in the sheet module, and had to copy it to each sheet on which I wanted the functionality. Now, I managed to reduce the code in the sheet module to a call to a subroutine in a standard module and it works, but I stil have to copy the code to each sheet on which I need the functionality. Is there a way to enter the code in only one place (a standard module, workbook module or class module) and have it function on any sheet in the workbook without code behind the sheet? Thank you for your help.

The ThisWorkbook code module has a Workbook_SheetBeforeDoubleClick event handler which you can use instead of capturing the event separately on each sheet.

You need an event handler on the sheet to take care of the double-click. Put this in the code behind each sheet you want to run the double-click code.
Option Explicit
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim sWorksheet As String
Dim sValue As String
Dim sCell As String
sWorksheet = ActiveSheet.Name
sValue = Target.Text
sCell = Target.Address
Call DoubleClicked(sWorksheet, sValue, sCell)
End Sub
Put your main routine in a module, and not on the sheet.
Option Explicit
Sub DoubleClicked(SheetName As String, CellText As String, CellAddress As String)
' your code goes here
MsgBox "You double-clicked cell " & CellAddress & " on sheet " _
& SheetName & ". The text in that cell is: " & CellText
End Sub
You don't want the logic repeated on each sheet. If you make a change, you have to change it for every single sheet. By simply calling the main routine from each sheet, you only have to write and maintain it once.

Related

Creating a Sheet List using Listbox

In a User form using a Listbox1 I would like to make a list of the Opened Workbooks and in the Listbox2 in the same form the sheets of the selected workbook in the listbox1 But also in the lisbox2 I would like to create with each sheet name a checkbox with five command buttons in the form to import, Export, Erase, Hide or Unhide the selected sheets from Wb1 to Wb2 and vice versa.
So far I receive assistance from you guys to make a do a form with a list of opened workbooks and a list of the respective worksheets here also I'm trying to get to work a code to import the sheets from one workbook to another here,. Do you know a Way to make this happen.
thank You
By the way this is the code use from a sheet to erase the sheets that might be put in the list
Sub DeleteSheets()
Dim wks As Worksheet
Dim MyRange As Range
Dim cell As Range
Set wks = Worksheets("Controls")
With wks
Set MyRange = Range("D5:D34", .Cells(.Rows.Count, "H").End(xlUp))
End With
On Error Resume Next
Application.DisplayAlerts = False
For Each cell In MyRange
Sheets(cell.Value).Delete
Next cell
Application.DisplayAlerts = True
On Error GoTo 0
Sheets("Controls").Range("D5:D34").ClearContents
End Sub
And this ones for hide and unhide the sheets:
Sub Hide_Sheets()
'In use
'Hide the sheets in Controls Sheet
Dim cell As Range
On Error Resume Next
For Each cell In Sheets("Controls").Range("E5:E34")
' Hide sheets
Sheets(cell.Value).Visible = False
Next cell
End Sub
Sub Unhide_Sheets()
'In use
'Unhide the sheets in Controls Sheet
Dim cell As Range
On Error Resume Next
For Each cell In Sheets("Controls").Range("G5:G34")
' Hide sheets
Sheets(cell.Value).Visible = True
Next cell
End Sub
I suggest the following structure of your userform:
Since you do what two lists one with workbooks and one with the worksheets of the currently selected workbook. Then you want to have five command buttons for the actions you want to perfom.
In order to update the workbooks and worksheets you will want to place code in the userform inside the Userform_activate and Listbox1_Change events. So You get the code to list all the workbook names into the listbox1 and put it into Userform_Activate. Evertime the userform is activated the list of workbooks will be updated.
If you now select a entry of listbox1 you want the code to update your sheet names. So you get the code to update the sheet names of a workbook with name "wbname" and put it into listbox1_Change. You then do the following code:
Private Sub ListBox1_Change()
Dim wbname as string
wbname=ListBox1.Value
call GetSheetNamesIntoListBox2(wbname)
End Sub
Where of course GetSheetNamesIntoListBox2 is the sub were you get all the sheetnames into ListBox2.
Lastly you need to setup the Buttons. Each Button has a Click Event which you will want to use. So if the Button is clicked the following code will run:
Private Sub CommandButton1_Click()
Dim wbname as string, wsname as string
wbname=Listbox1.Value
wsname=Listbox2.Value
' You may want to check if wbname and wsname are valid before running the Task
PerformAction(wbname,wsname)
End Sub
Where PerformAction is the routine you use to import, export, clear, hide or unhide the sheet.
I know it is no workedout solution but you will be able to adjust this general solution to your specific case. If you run into problems using my approach just ask in the comments.

VBA causing typed information to go to an incorrect worksheet

I am creating a spreadsheet that creates a reference number on the first worksheet (called database, to be used similarly to a database) and generates a new worksheet. This then gives a reference number on the new worksheet so that they are linked together. This is done by pressing "New Idea" on a UserForm.
Once this is completed it should then go to the newly created worksheet and highlight cell C7. Once this is complete it should close the UserForm and allow the user to be able to type in cell C7 on the new worksheet with no further steps needed.
This works fine if I use F8 to step through the process however if I close the code window and run through the process as a user would it doesn't work as it should.
C7 is highlighted but once you have typed in it and press enter to go to the cell below, what you've typed disappears completely, and whatever you type on the newly generated worksheet is actually entered on another worksheet.
I have a seperate worksheet that contains a button to open the UserForm and all data that is entered on the newly generated worksheet goes to this sheet incorrectly.
My code is below, and is all within the UserForm's code. I have left the ComboBox code below but this isn't relevant to the generation of the new worksheets. All that does is list the created tabs so the user can select a worksheet from the UserForm and go directly there rather than having to scroll sideways.
I'm using Excel 2013. I'm by no means a VBA veteran so any help would be greatly appreciated!
Thanks!
Private Sub UserForm_Initialize()
Me.ComboBox1.List = Worksheets("Database").Range("A2:A10000").Value
End Sub
Private Sub CreateNewIdea_Click()
CopySheet
End Sub
Sub CopySheet()
Dim LastRow As Long
NewReference
LastRow = Sheets("Database").Range("A" & Rows.Count).End(xlUp).Row - 1
ReturnValue = LastRow
Sheets("Idea Template").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = LastRow
Range("C3").Value = LastRow
Me.ComboBox1.List = Worksheets("Database").Range("A2:A10000").Value
Range("C7").Select
Unload Home
End Sub
Sub NewReference()
Dim LastRow As Long
LastRow = Sheets("Database").Range("A" & Rows.Count).End(xlUp).Row
Sheets("Database").Cells(LastRow + 1, "A").Value = Sheets("Database").Cells(LastRow, "A").Value + 1
End Sub
Private Sub ComboBox1_Change()
Worksheets(ComboBox1.Text).Select
End Sub
I've taken the liberty to edit and rewrite the code you've written for greater flexibility.
Option Explicit 'Forces the variable to be declared, undeclared variables are not allowed
Dim DatabaseTable As ListObject 'Makes the variable usable for the entire module
Dim Lastrow As Long
Private Sub UserForm_Initialize()
Set DatabaseTable = ThisWorkbook.Worksheets("Database").ListObjects("References")
'I'm assuming you've formatted the data on the worksheet as a table and named the table "References"
Dim i As Long
Dim DatabaseRows As Long
DatabaseRows = DatabaseTable.ListRows.Count
With Me.ComboBox1
.Value = Empty
.Clear
For i = 1 To DatabaseRows
.AddItem DatabaseTable.DataBodyRange(i, 1)
Next i
End With
End Sub
Private Sub CreateNewIdea_Click()
Set DatabaseTable = ThisWorkbook.Worksheets("Database").ListObjects("References")
Call CopySheet
End Sub
Sub CopySheet() 'Are you calling Sub CopySheet() from other subs besides Private Sub CreateNewIdea_Click()?
Call NewReference
Dim ReturnValue As Long 'I'm declaring this variable because I'm using the option explicit and that doesn't allow undeclared variables
ReturnValue = Lastrow 'Unless ReturnValue is a public variable, it's not doing much here.
ThisWorkbook.Worksheets("Idea Template").Copy After:=ThisWorkbook.Worksheets(Worksheets.Count)
ThisWorkbook.Worksheets("Idea Template (2)").name = Lastrow
ThisWorkbook.Worksheets(CStr(Lastrow)).Cells(1, 3).Value = Lastrow 'Cstr(lastrow) is needed because we want the sheet with the name of the last row, not the nth sheet which is what happens with WorkSheets(Lastrow) as lastrow is a number
Call UserForm_Initialize 'Calls the procedure which fills ComboBox1, if the unload home refers to this form, then this line is redundant since the combobox is filled again when the form is initialized.
ThisWorkbook.Worksheets(CStr(Lastrow)).Cells(7, 3).Select
Unload Home 'If the name of this form is home, you can just 'Unload Me'
End Sub
Sub NewReference() 'Are you calling Sub NewReference from other subs besides Sub CopySheet()?
DatabaseTable.ListRows.Add AlwaysInsert:=False 'Adds a new row to the table on the worksheet "Database"
Lastrow = DatabaseTable.ListRows.Count
If Lastrow = 2 And IsEmpty(DatabaseTable.DataBodyRange(1, 1)) Then 'This if determines if a row was added while the first row does not contain a reference
DatabaseTable.DataBodyRange(Lastrow, 1).Value = 1 'First reference, can be anything you want really
DatabaseTable.ListRows(1).Delete 'First row is deleted, otherwise you'd have an empty first row
Lastrow = Lastrow - 1
Else
DatabaseTable.DataBodyRange(Lastrow, 1).Value = DatabaseTable.DataBodyRange(Lastrow - 1, 1) + 1
End If
End Sub
Private Sub ComboBox1_Change()
If Not Me.ComboBox1 = Empty Then
Worksheets(CStr(Me.ComboBox1)).Select
End If
End Sub
Revised answer
After looking at the document provided by #tomjo and trying to reporduce the problem I found that the problem was caused by the buttons on the sheets. The buttons used were the Form Controls rather than ActiveX Controls.
A macro was assigned to the Form Control The macro was defined in a module as you'd expect. The Macro only called to show the relevant form. It appeared as if the selected sheet, either by the menu on the form or after creating a new sheet from the form wasn't properly activated and the information entered in the sheet that showing was actually entered in the form which was last manually selected. Stepping through the code I found that the proper sheet and cell was active after the selection through the form through Debug.Print ThisWorkbook.ActiveSheet.Name, ThisWorkbook.ActiveSheet.ActiveCell.Address. I failed to discover why, while the correct sheet and cell were active, the information was entered in the last manually selected worksheet and cell.
To verify that the issue was not caused by the file somehow I tried to reproduce the problem with an entirely new file. Again the problem occurred that while the screen was showing the correct sheet and cell as selected (selected through a form, called by a sub in a module, called by a Form Control) the information was actually entered in the last manually selected sheet and cell.
----Edit----
Running the Showform macro (which calls the form to show) from the Macros button under the developer tab in the ribbon, instead of clicking the Form control button which has the ShowForm macro assigned to it, doesn't create the issue.
----End of Edit----
I then only removed the Form control, and the sub in the module which calls to show the form and placed an ActiveX Control (CommandButton) on the sheet and on the relevant Sheet module created:
Private Sub CommandButton1_Click()
Form.Show
End Sub
Without editing the code any further, there were no further issues regarding information being entered on the last manually selected sheet and cell instead of the sheet and cell the screen was showing as selected.
Edited file (The link will be active for a limited time)

Moving sheets with VBA codes and buttons to a new Workbook

I have a worksheet with macros and buttons. I want to copy that worksheet to other workbooks so that the macros can be used with the data in the new workbook.
My problem is that when I copy a sheet into a new workbook, the macros come with it, but the button on the sheet still points to the macros in the original workbook. You have to right mouse click "Assign Macro" and choose the macro in the current workbook.
Is there a property setting that tells the button in Excel to use the macro in the new workbook instead of the old workbook?
Here's a solution I pulled from TheSpreadsheetGuru. I modified it to accept a sheet object so you can run it against any sheet you have a handle on.
Public Sub FixCopiedButtonMacroLinks(ByVal theSheet As Worksheet)
' UPDATED: by HackSlash to accept a worksheet parameter.
'PURPOSE: Remove an external workbook reference from all shapes triggering macros
'Source: www.TheSpreadsheetGuru.com
Dim control As Shape
Dim MacroLink As String
Dim SplitLink As Variant
Dim NewLink As String
'Loop through each shape in worksheet
For Each control In theSheet.Shapes
'Grab current macro link (if available)
MacroLink = control.OnAction
'Determine if shape was linking to a macro
If MacroLink <> vbNullString And InStr(MacroLink, "!") <> 0 Then
'Split Macro Link at the exclaimation mark (store in Array)
SplitLink = Split(MacroLink, "!")
'Pull text occurring after exclaimation mark
NewLink = SplitLink(1)
'Remove any straggling apostrophes from workbook name
If Right(NewLink, 1) = "'" Then
NewLink = Left(NewLink, Len(NewLink) - 1)
End If
'Apply New Link
control.OnAction = NewLink
End If
Next control
End Sub
You can include a line in your macro that re-assigns the control to the macro in its own workbook instead of the originating workbook.
What you can do is just record the act of assigning the macro, for example:
ActiveSheet.Shapes.Range(Array("Button 1")).Select
Selection.OnAction = "Macro1"
Then get rid of the pesky "select" stuff:
ActiveSheet.Shapes.Range(Array("Button 1")).OnAction = "Macro1"
And there you have it. Of course the button and macro names with vary depending on your workbook, but it will be close to that.
Your question is somewhat vague. I will then guess.
You have to:
Make sure that the Subs to be run are actually contained in the Worksheet to be copied. Press Alt+F11 to open the VB Editor, and you will see on the left the Sheet and other modules.
Inspect the code, and make sure all references to macros are not prefixed with any Sheet/Workbook name.

application.run vba not working with sheet macros

My file has different sheets with the same-named sub routine inside each doing different things specific to the sheet. So, I'm trying to call dynamically a macro inside a selected sheet using Application.Run from a module. All sheets' "object" name (I don't know how to call those) are already modified to be the same as its view (inside the bracket) name, without the spaces.
macro = Chr(39) & ThisWorkbook.Name & Chr(39) & "!" & Replace(SheetName, " ", "") & "." & "Harmoniser()"
Application.Run macro
Here's an example of the Harmoniser macro in a Sheet.
Sub Harmoniser()
ActiveSheet.Range("k22").GoalSeek Goal:=0, ChangingCell:=Range("l13")
MsgBox ("Done!")
End Sub
Somehow, only the MsgBox works, and it shows it twice everytime. Debugging by putting a break point doesn't work either. It happens to all of my sheets. Is there a limitation to Application.Run that it only allows certain code or there's an error that Excel is not giving me?
I get the same thing when I run your code. I tried a few different tweaks and am able to enter debug/breakpoint mode if I do this:
Sub t()
Dim sht As Worksheet
Dim macro As String
For Each sht In ThisWorkbook.Worksheets
sht.Activate
macro = sht.CodeName & ".Harmoniser"
Application.Run macro
Next
End Sub
Your code may have been erroring silently because you were using ActiveSheet without first Activating the sheet which contains the macro to be run. Not sure about the double-display of the MsgBox, but seems like that may be related.

Copy Multiple Ranges from userform to worksheet in another workbook

I am having a hard time with an issue I have with an excel workbook im trying to create that will parse multiple ranges defined in a userform from another workbook. My issue is that when i am attempting to paste the range in the form, the code will only paste the string and not the code itself.
Note: this is a mod attempt of Jan Karel Pieterse's range userform workaround
code below:
Private Sub cmbOK_Click() 'VALIDATION OF RANGE
Dim wbk As Workbook
Dim wst As Worksheet
Dim vbk As Variant
vbk = "[" & Me.cbxWorkbooks.Value & "]" & Me.refSelectCells.Value
If refSelectCells.Text <> "" Then
If TypeName(Selection) = "Range" Then
If IsValidRef(refSelectCells.Text) Then
OK = True
Windows("abc_123_Review_Automation.xlsm").Activate
Worksheets("CalculationSheet").Range("A2") = vbk.Value
End If
End If
End If
Me.Hide
End Sub
Example:
if vbk's value is "[Workbook]sheet1!a4:j365"
I want to paste the contents of that range address into a new workbook at "A2"
Please Help!
Range("A2") returns a range object.
If you want to modify the formula of the cell you should modify Range("A2").FormulaR1C1.
If you want to modify the value you should modifyRange("A2").Value.