I relatively new to VBA. Here is what I need to accomplish:
I need code to run when the value of some cells changes in some worksheets. I just learned how to do this using events.
The problem I'm having is that the worksheets that need the event are not there to begin with, but are generated by a macro. Also, their number might vary depending on the data (i.e. sometimes the macro might generate 3, sometimes 10.. and will rename them using some cell values, so also the names differ every time).
From what I can see, the event that triggers the macro on cell change needs to be coded for the object itself (specific worksheets). Is there a way to do it for worksheets that are macro-generated, and don't exist in the workbook when first opened?
Any help would be much appreciated!
A tiny trick:
The usual way to add a sheet is to use code like:
Sheets.Add
which will get you a fresh new sheet, but this new sheet will have no event code associated with it.
Instead create a template worksheet (called templ) and in this template worksheet include all necessary event macros. Now, instead of Adding a new worksheet, just:
Sheets("templ").Copy after:=Sheets(Sheets.Count)
you get a new worksheet, but with all macros attached!
Related
I have an excel spreadsheet with a very complex macro. The spreadsheet takes a file (imported through a button on the sheet), and runs statistics on it. is there any way to automate the macro to run on multiple files in a folder?
Thanks in advance
The macro identifies the worksheets from which it draws data and these sheets are in a workbook, which is also identified. Hence, by changing the name of the source(s) in the code, perhaps even dynamically, you can make the same macro perform its magic on other workbooks.
With that said, since VBA absolutely needs a workbooks to identify a worksheet and must have a worksheet in order to identify a cell, VBA will provide a default for either if the code doesn't mention another. Therefore the innocent Cells(1, 1) or Range("A1:B10") you may see in your code in fact stand for ActiveWorkbook.ActiveSheet.Cells(1, 1) or ActiveWorkbook.ActiveSheet.Range("A1:B10"). Therefore, if you want to change the default workbook for another the process is to first introduce a variable to specify the workbook and worksheet and then change the object assigned to that variable.
In a less generic way, let me presume that you don't have syntax like Range("A1:B10") in your code at all but Worksheets("Sheet1").Range("A1:B10"), identifying the worksheet but not the workbook. Let me further presume that the sheet names are the same in all the workbooks on which you want to run the code. In that case you would make the change as shown below.
Dim Wb As Workbook
Set Wb = ActiveWorkbook 'or perhaps ThisWorkbook (=the one having the code)
' and then change all applicable instances to
Wb.Worksheets("Sheet1").Range("A1:B10")
Now you can change the code to specify another workbook simply with:-
Set Wb = Workbooks("My workbook.xlsm")
I'm currently working on a macro that modifies a standard format of spreadsheet. This will be used for many spreadsheets by several people in my company. So I've created an addin that everyone should be able to install and use.
However not all the spreadsheets will have the same name for each sheet.
Originally I tried referring to the sheets as
Sheet1.Range...
This gives
Run-time error '424':
Object required.
I can make it work with
Worksheets('name_of_worksheet').Range
But this requires the user to change that every time they wish to use the addin in a different spreadsheet, or to rename the sheets.
Is there another way of referencing the 'first' and 'second' worksheets? They will always be in that order and created in that order.
I believe this is to do with having saved the macro as an addin since I've never had this issue.
Thank you!
Edit:
The reformatting of the spreadsheet depends on data found in sheet 1 and sheet 2. So within the macro I need to be able to refer to both of those sheets. The name of the sheets will not always be the same, so I would rather not have to use:
Worksheets('name_of_worksheet').Range
Using Active Sheet is also not ideal since there will only be 1 active sheet, and I need to access both sheets.
Sheets(1).Range
does not work either, I get the error:
Run-time error '424':
Object required.
You could make a ugly hack and loop two sheets.
i = 1
For Each Sht In ThisWorkbook.Sheets
If i = 3 Then Exit For
Debug.Print Sht.Name
i = i + 1
Next Sht
This loops all sheets and when two sheets has been looped the code exits the for loop.
In the loop you could save the sheet names to variables or perhaps use this as the base of your code and add your code instead of the debug line.
I am trying to print the active worksheet and another worksheet with general information (in the same workbook) at the same time. (Recto verso, which my printer does automatically, no code needed for that)
In my workbook I have multiple sheets that use the same code for printing. Now I would like to add the sheet with general information called "Huurvoorwaarden" to an array so it is printed automaticaly and at the back side of the active sheet.
I have tried multiple sollutions like dim / set activesheet.name, and codes which I have found on the web. nothing works.
I know that when I would change "activesheet" to Sheet1, that would work, but only for sheet 1.
Could you please help me?
Here is what I have got: (all my older attempts are deleted)
'Print Active Sheet and sheet Huurvoorwaarden
Worksheets(Array("activesheet.name", "Huurvoorwaarden")).PrintOut
The name of the active sheet is not the string literal "ActiveSheet.Name", it is the property ActiveSheet.Name.
So you need to use
Worksheets(Array(Activesheet.Name, "Huurvoorwaarden")).PrintOut
I have a workbook (we'll call it "AAA") that is based on a template and refers to code modules in a second workbook. The second workbook (we'll call it "CodeStorage") is a repository for code modules so that any saved versions of the template will be able to access modified/updates code without a change to the saved workbook.
My problem arises when I have multiple windows open in "AAA" and try to get the activesheet when a module is running in "CodeStorage". In code, I create an object ("oWorkbook") that is a reference to the workbook "AAA" When "AAA" has focus, oWorkbook.Activesheet returns the sheet for the active window. When "CodeStorage" is running a module and thus has focus, oWorkbook.Activesheet returns the sheet that is selected in window #1 regardless of what window (#2, 3, etc) was active when the code module in "CodeStorage" was called.
Has anyone run into this and have you found a work around?
ActiveSheet is confusing the way you are using it.
You need to explicitly activate a sheet for it to be considered the ActiveSheet. Running code in another workbook does not activate it.
Selecting cells in a worksheet will activate it. Or specifically calling Activate.
You could do something like:
oWorkbook.Activate
oWorkbook.Activesheet
Alternatively, and preferably, you could do something like the following:
oWorkbook.Worksheets("Sheet1")
oWorkbook.Worksheets(1)
Both these are better. If your user selects a different workbook during runtime execution or you select something in a different sheet, ActiveSheet will return something different.
It's better to fully qualify you workbook paths when using multiple workbooks. This will save you a ton of headache in the future for dealing with "what is activated?" or "what is selected?" This answer is worth reading, too.
To rephrase my question, I was looking for a way to reference the last active worksheet in a workbook that had multiple windows open. If window 1 was selected, I wanted the sheet from window 1, same for window 2, 3, or ???. I also needed the reference when a code module in a different workbook was running and the code module had a variable that was an object reference to the calling workbook.
The solution is Workbook.Windows.Item(1).ActiveSheetView.Sheet. When called from the running code module, even in a different workbook, it returns the same as workbook.activesheet when that is called from the workbook itself. My tests show that Workbook.Windows.Item(1) is the last active window when a workbook loses focus.
Thank you enderland. You got me pointed in the right direction.
I have 10 XLS's, each of which contain a a few hundred lines of VBA that do the same thing. I want to move this common code into an 11th XLS, and have the other 10 call the code in the 11th XLS. The common code must have access to all of the data and worksheets in the calling XLS. This last requirement does not seem to be addressed by other answers to this question on SO. Can I pass the calling XLS's worksheets in as a parameter, or something similar?
Instead of putting this into a secondary XLS file, I'd recommend creating an XLA file (an Excel Add In).
This is the exact scenario for which XLA was intended. XLA will work the way you intend in this case.
For details on creating an XLA, see this page.
Yes, you can pass references to workbooks, worksheets, ranges, etc. as parameters to any function:
Public Sub CallMe(ByVal oWorkbook as Workbook)
Dim oWorksheet as Worksheet
Set oWorksheet = oWorkbook.Worksheets(1)
' Do stuff...
End Sub
Note that you'll probably have to re-write a lot of the code you copy from the 10 workbooks since they'll be full of implicit references to "this" workbook, such as Worksheets(1) etc. As in the example above, you now need to say oWorkbook.Workbooks(1) instead.